Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Sử dụng các chuỗi để thực hiện các yêu cầu cơ sở dữ liệu

Quy tắc phân luồng cho JavaFX

Có hai quy tắc cơ bản cho luồng và JavaFX:

  1. Bất kỳ mã nào sửa đổi hoặc truy cập trạng thái của nút là một phần của biểu đồ cảnh đều phải được thực thi trên chuỗi ứng dụng JavaFX. Một số hoạt động khác (ví dụ:tạo Giai đoạn mới s) cũng bị ràng buộc bởi quy tắc này.
  2. Bất kỳ mã nào có thể mất nhiều thời gian để chạy nên được thực thi trên một chuỗi nền (tức là không phải trên Chuỗi ứng dụng FX).

Lý do cho quy tắc đầu tiên là, giống như hầu hết các bộ công cụ giao diện người dùng, khung được viết mà không có bất kỳ sự đồng bộ nào về trạng thái của các phần tử của đồ thị cảnh. Việc thêm đồng bộ hóa sẽ phát sinh chi phí hiệu suất và điều này hóa ra lại là một chi phí quá lớn đối với các bộ công cụ giao diện người dùng. Vì vậy, chỉ một luồng có thể truy cập trạng thái này một cách an toàn. Vì luồng giao diện người dùng (luồng ứng dụng FX cho JavaFX) cần truy cập trạng thái này để hiển thị cảnh, nên luồng ứng dụng FX là luồng duy nhất mà bạn có thể truy cập trạng thái biểu đồ cảnh "trực tiếp". Trong JavaFX 8 trở lên, hầu hết các phương thức tuân theo quy tắc này thực hiện kiểm tra và ném các ngoại lệ thời gian chạy nếu quy tắc bị vi phạm. (Điều này trái ngược với Swing, nơi bạn có thể viết mã "bất hợp pháp" và mã có vẻ chạy tốt, nhưng trên thực tế dễ bị lỗi ngẫu nhiên và không thể đoán trước vào thời điểm tùy ý.) Đây là nguyên nhân của IllegalStateException bạn đang nhìn thấy :bạn đang gọi courseCodeLbl.setText (...) từ một chuỗi khác với Chuỗi ứng dụng FX.

Lý do cho quy tắc thứ hai là Luồng ứng dụng FX, cũng như chịu trách nhiệm xử lý các sự kiện của người dùng, cũng chịu trách nhiệm hiển thị cảnh. Vì vậy, nếu bạn thực hiện một hoạt động lâu dài trên luồng đó, giao diện người dùng sẽ không được hiển thị cho đến khi hoạt động đó hoàn tất và sẽ không phản hồi với các sự kiện của người dùng. Mặc dù điều này sẽ không tạo ra ngoại lệ hoặc gây ra trạng thái đối tượng bị hỏng (như vi phạm quy tắc 1 sẽ xảy ra), nhưng nó (tốt nhất là) tạo ra trải nghiệm người dùng kém.

Vì vậy, nếu bạn có một hoạt động lâu dài (chẳng hạn như truy cập cơ sở dữ liệu) cần cập nhật giao diện người dùng khi hoàn thành, kế hoạch cơ bản là thực hiện hoạt động dài hạn trong một chuỗi nền, trả lại kết quả của hoạt động khi nó hoàn thành, sau đó lên lịch cập nhật giao diện người dùng trên chuỗi Giao diện người dùng (Ứng dụng FX). Tất cả các bộ công cụ giao diện người dùng đơn luồng đều có cơ chế để thực hiện điều này:trong JavaFX, bạn có thể làm như vậy bằng cách gọi Platform.runLater (Runnable r) để thực thi r.run () trên Chuỗi ứng dụng FX. (Trong Swing, bạn có thể gọi SwingUtilities.invokeLater (Runnable r) để thực thi r.run () trên chuỗi điều phối sự kiện AWT.) JavaFX (xem phần sau trong câu trả lời này) cũng cung cấp một số API cấp cao hơn để quản lý giao tiếp trở lại Chuỗi ứng dụng FX.

Thực tiễn tốt chung cho đa luồng

Phương pháp tốt nhất để làm việc với nhiều luồng là tạo mã cấu trúc được thực thi trên một luồng "do người dùng xác định" như một đối tượng được khởi tạo với một số trạng thái cố định, có một phương thức để thực hiện hoạt động và khi hoàn thành trả về một đối tượng đại diện cho kết quả. Sử dụng các đối tượng không thay đổi cho trạng thái khởi tạo và kết quả tính toán là rất mong muốn. Ý tưởng ở đây là loại bỏ khả năng có thể nhìn thấy bất kỳ trạng thái có thể thay đổi nào từ nhiều luồng càng xa càng tốt. Truy cập dữ liệu từ cơ sở dữ liệu rất phù hợp với thành ngữ này:bạn có thể khởi tạo đối tượng "worker" của mình với các tham số cho quyền truy cập cơ sở dữ liệu (cụm từ tìm kiếm, v.v.). Thực hiện truy vấn cơ sở dữ liệu và nhận tập hợp kết quả, sử dụng tập hợp kết quả để điền vào một tập hợp các đối tượng miền và trả về tập hợp ở cuối.

Trong một số trường hợp, cần phải chia sẻ trạng thái có thể thay đổi giữa nhiều luồng. Khi điều này hoàn toàn phải được thực hiện, bạn cần phải cẩn thận đồng bộ hóa quyền truy cập vào trạng thái đó để tránh quan sát trạng thái ở trạng thái không nhất quán (có những vấn đề khác phức tạp hơn cần được giải quyết, chẳng hạn như độ sống của trạng thái, v.v.). Khuyến nghị mạnh mẽ khi điều này là cần thiết là sử dụng thư viện cấp cao để quản lý những phức tạp này cho bạn.

Sử dụng API javafx.concurrent

JavaFX cung cấp một API đồng thời được thiết kế để thực thi mã trong một chuỗi nền, với API được thiết kế đặc biệt để cập nhật giao diện người dùng JavaFX khi hoàn thành (hoặc trong khi) thực thi mã đó. API này được thiết kế để tương tác với java.util.concurrent API , cung cấp các phương tiện chung để viết mã đa luồng (nhưng không có móc giao diện người dùng). Lớp khóa trong javafx.concurrent Nhiệm vụ , đại diện cho một đơn vị công việc duy nhất, một lần, dự định được thực hiện trên một chuỗi nền. Lớp này định nghĩa một phương thức trừu tượng duy nhất, call () , không nhận tham số, trả về một kết quả và có thể ném các ngoại lệ đã kiểm tra. Nhiệm vụ triển khai Runnable với run () của nó phương thức chỉ cần gọi call () . Nhiệm vụ cũng có một bộ sưu tập các phương thức được đảm bảo cập nhật trạng thái trên Chuỗi ứng dụng FX, chẳng hạn như updateProgress (...) , updateMessage (...) , v.v. Nó xác định một số thuộc tính có thể quan sát được (ví dụ: trạng thái giá trị ):người nghe các thuộc tính này sẽ được thông báo về các thay đổi trên Chuỗi ứng dụng FX. Cuối cùng, có một số phương pháp tiện lợi để đăng ký trình xử lý ( setOnSucceeded (...) , setOnFailed (...) , vân vân); bất kỳ trình xử lý nào được đăng ký thông qua các phương thức này cũng sẽ được gọi trên Chuỗi ứng dụng FX.

Vì vậy, công thức chung để lấy dữ liệu từ cơ sở dữ liệu là:

  1. Tạo Tác vụ để xử lý lệnh gọi đến cơ sở dữ liệu.
  2. Khởi tạo Tác vụ với bất kỳ trạng thái nào cần thiết để thực hiện lệnh gọi cơ sở dữ liệu.
  3. Triển khai call () của nhiệm vụ để thực hiện lệnh gọi cơ sở dữ liệu, trả về kết quả của cuộc gọi.
  4. Đăng ký một trình xử lý với nhiệm vụ gửi kết quả đến giao diện người dùng khi nó hoàn tất.
  5. Gọi nhiệm vụ trên một chuỗi nền.

Để truy cập cơ sở dữ liệu, tôi thực sự khuyên bạn nên đóng gói mã cơ sở dữ liệu thực tế trong một lớp riêng biệt mà không biết gì về giao diện người dùng ( Mẫu thiết kế đối tượng truy cập dữ liệu ). Sau đó, chỉ cần có tác vụ gọi các phương thức trên đối tượng truy cập dữ liệu.

Vì vậy, bạn có thể có một lớp DAO như thế này (lưu ý không có mã giao diện người dùng ở đây):

public class WidgetDAO {

    // In real life, you might want a connection pool here, though for
    // desktop applications a single connection often suffices:
    private Connection conn ;

    public WidgetDAO() throws Exception {
        conn = ... ; // initialize connection (or connection pool...)
    }

    public List<Widget> getWidgetsByType(String type) throws SQLException {
        try (PreparedStatement pstmt = conn.prepareStatement("select * from widget where type = ?")) {
            pstmt.setString(1, type);
            ResultSet rs = pstmt.executeQuery();
            List<Widget> widgets = new ArrayList<>();
            while (rs.next()) {
                Widget widget = new Widget();
                widget.setName(rs.getString("name"));
                widget.setNumberOfBigRedButtons(rs.getString("btnCount"));
                // ...
                widgets.add(widget);
            }
            return widgets ;
        }
    }

    // ...

    public void shutdown() throws Exception {
        conn.close();
    }
}

Việc truy xuất một loạt các widget có thể mất nhiều thời gian, vì vậy bất kỳ lệnh gọi nào từ một lớp giao diện người dùng (ví dụ:một lớp bộ điều khiển) nên lên lịch cho việc này trên một chuỗi nền. Một lớp bộ điều khiển có thể trông giống như sau:

public class MyController {

    private WidgetDAO widgetAccessor ;

    // java.util.concurrent.Executor typically provides a pool of threads...
    private Executor exec ;

    @FXML
    private TextField widgetTypeSearchField ;

    @FXML
    private TableView<Widget> widgetTable ;

    public void initialize() throws Exception {
        widgetAccessor = new WidgetDAO();

        // create executor that uses daemon threads:
        exec = Executors.newCachedThreadPool(runnable -> {
            Thread t = new Thread(runnable);
            t.setDaemon(true);
            return t ;
        });
    }

    // handle search button:
    @FXML
    public void searchWidgets() {
        final String searchString = widgetTypeSearchField.getText();
        Task<List<Widget>> widgetSearchTask = new Task<List<Widget>>() {
            @Override
            public List<Widget> call() throws Exception {
                return widgetAccessor.getWidgetsByType(searchString);
            }
        };

        widgetSearchTask.setOnFailed(e -> {
           widgetSearchTask.getException().printStackTrace();
            // inform user of error...
        });

        widgetSearchTask.setOnSucceeded(e -> 
            // Task.getValue() gives the value returned from call()...
            widgetTable.getItems().setAll(widgetSearchTask.getValue()));

        // run the task using a thread from the thread pool:
        exec.execute(widgetSearchTask);
    }

    // ...
}

Lưu ý cách gọi phương thức DAO chạy dài (có khả năng) được bao bọc trong một Tác vụ được chạy trên một chuỗi nền (thông qua trình truy cập) để ngăn chặn việc chặn giao diện người dùng (quy tắc 2 ở trên). Bản cập nhật cho giao diện người dùng ( widgetTable.setItems (...) ) thực sự được thực thi trở lại Chuỗi ứng dụng FX, sử dụng Tác vụ Phương thức gọi lại tiện lợi của setOnSucceeded (...) (thỏa mãn quy tắc 1).

Trong trường hợp của bạn, quyền truy cập cơ sở dữ liệu mà bạn đang thực hiện trả về một kết quả duy nhất, vì vậy bạn có thể có một phương thức như

public class MyDAO {

    private Connection conn ; 

    // constructor etc...

    public Course getCourseByCode(int code) throws SQLException {
        try (PreparedStatement pstmt = conn.prepareStatement("select * from course where c_code = ?")) {
            pstmt.setInt(1, code);
            ResultSet results = pstmt.executeQuery();
            if (results.next()) {
                Course course = new Course();
                course.setName(results.getString("c_name"));
                // etc...
                return course ;
            } else {
                // maybe throw an exception if you want to insist course with given code exists
                // or consider using Optional<Course>...
                return null ;
            }
        }
    }

    // ...
}

Và sau đó mã bộ điều khiển của bạn sẽ giống như

final int courseCode = Integer.valueOf(courseId.getText());
Task<Course> courseTask = new Task<Course>() {
    @Override
    public Course call() throws Exception {
        return myDAO.getCourseByCode(courseCode);
    }
};
courseTask.setOnSucceeded(e -> {
    Course course = courseTask.getCourse();
    if (course != null) {
        courseCodeLbl.setText(course.getName());
    }
});
exec.execute(courseTask);

tài liệu API cho Task có nhiều ví dụ khác, bao gồm cập nhật tiến trình thuộc tính của nhiệm vụ (hữu ích cho thanh tiến trình ..., v.v.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nhận đầu ra cơ sở dữ liệu MySQL qua PHP sang XML

  2. Làm cách nào để viết SQL cho một bảng có cùng tên với một từ khóa được bảo vệ trong MySql?

  3. MyCLI - Ứng dụng khách MySQL / MariaDB với tính năng Tự động hoàn thành và tô sáng cú pháp

  4. SQL SELECT để lấy N số nguyên dương đầu tiên

  5. Cách tìm bản ghi trùng lặp trong MySQL