2013-02-14 24 views
10

Tôi tự hỏi liệu có ai có thể giúp tôi với một vấn đề khá khó chịu về việc tạo một chuỗi nền trong JavaFX không! Tôi hiện đang có một số truy vấn SQL thêm dữ liệu vào giao diện người dùng hiện đang chạy trên Chủ đề ứng dụng JavaFX (xem ví dụ bên dưới). Tuy nhiên, khi mỗi truy vấn này thực thi nó sẽ đóng băng giao diện người dùng vì nó không chạy trên một chuỗi nền. Tôi đã xem xét các ví dụ khác nhau sử dụng Task và loại hiểu chúng nhưng tôi không thể làm cho chúng hoạt động khi thực hiện các truy vấn cơ sở dữ liệu, một số trong số đó mất vài giây để chạy.JavaFX - Chủ đề nền cho truy vấn SQL

Đây là một trong những phương pháp mà thực hiện một truy vấn:

public void getTopOrders() { 
    customerOrders.clear(); 
    try { 
     Connection con = DriverManager.getConnection(connectionUrl); 
     //Get all records from table 
     String SQL = "EXEC dbo.Get_Top_5_Customers_week"; 
     ResultSet rs; 
     try (Statement stmt = con.createStatement();) { 
      rs = stmt.executeQuery(SQL); 

      while (rs.next()) { 
       double orderValue = Double.parseDouble(rs.getString(3)); 
       customerOrders.add(new CustomerOrders(rs.getString(1), 
         rs.getString(2), "£" + formatter.format(orderValue), 
         rs.getString(4).substring(6, 8) + "/" + 
         rs.getString(4).substring(4, 6) + "/" + 
         rs.getString(4).substring(0, 4))); 
      } 
     } 

    } catch (SQLException | NumberFormatException e) { 
    } 
} 

Mỗi bản ghi xử lý được thêm vào một ObservableList được liên kết với một TableView, hoặc biểu đồ hoặc chỉ đơn giản là thiết lập văn bản trên một nhãn (phụ thuộc vào truy vấn). Làm thế nào tôi có thể thực hiện truy vấn trên một sợi nền và vẫn rời khỏi giao diện miễn phí để sử dụng và được cập nhật từ các truy vấn

Cảm ơn trước

+1

Bạn có quan sát các lớp học trong gói javafx.concurrent không? docs.oracle.com/javafx/2/api/javafx/concurrent/…. Tôi nghĩ, một lớp Task đặc biệt thú vị đối với bạn: docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html, bạn có thể đọc javadoc của nó, để hiểu danh sách đầy đủ các tùy chọn. –

+0

Có lẽ DataFX có thể giúp bạn: http://www.javafxdata.org hoặc http://www.guigarage.com/category/datafx/ –

Trả lời

14

Tôi tạo ra một sample solution cho việc sử dụng một Task (như đề xuất trong bình luận của Alexander Kirov của) để truy cập cơ sở dữ liệu trên một chuỗi thực hiện đồng thời với chuỗi ứng dụng JavaFX.

Các bộ phận có liên quan của dung dịch mẫu được sao chép dưới đây:

// fetches a collection of names from a database. 
class FetchNamesTask extends DBTask<ObservableList<String>> { 
    @Override protected ObservableList<String> call() throws Exception { 
    // artificially pause for a while to simulate a long 
    // running database connection. 
    Thread.sleep(1000); 

    try (Connection con = getConnection()) { 
     return fetchNames(con); 
    } 
    } 

    private ObservableList<String> fetchNames(Connection con) throws SQLException { 
    logger.info("Fetching names from database"); 
    ObservableList<String> names = FXCollections.observableArrayList(); 

    Statement st = con.createStatement();  
    ResultSet rs = st.executeQuery("select name from employee"); 
    while (rs.next()) { 
     names.add(rs.getString("name")); 
    } 

    logger.info("Found " + names.size() + " names"); 

    return names; 
    } 
} 

// loads a collection of names fetched from a database into a listview. 
// displays a progress indicator and disables the trigge button for 
// the operation while the data is being fetched. 
private void fetchNamesFromDatabaseToListView(
     final Button triggerButton, 
     final ProgressIndicator databaseActivityIndicator, 
     final ListView listView) { 
    final FetchNamesTask fetchNamesTask = new FetchNamesTask(); 
    triggerButton.setDisable(true); 
    databaseActivityIndicator.setVisible(true); 
    databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty()); 
    fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() { 
    @Override public void handle(WorkerStateEvent t) { 
     listView.setItems(fetchNamesTask.getValue()); 
    } 
    }); 
    fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() { 
    @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) { 
     if (!isRunning) { 
     triggerButton.setDisable(false); 
     databaseActivityIndicator.setVisible(false); 
     } 
    }; 
    }); 
    databaseExecutor.submit(fetchNamesTask); 
} 

private Connection getConnection() throws ClassNotFoundException, SQLException { 
    logger.info("Getting a database connection"); 
    Class.forName("org.h2.Driver"); 
    return DriverManager.getConnection("jdbc:h2:~/test", "sa", ""); 
} 

abstract class DBTask<T> extends Task<T> { 
    DBTask() { 
    setOnFailed(new EventHandler<WorkerStateEvent>() { 
     @Override public void handle(WorkerStateEvent t) { 
     logger.log(Level.SEVERE, null, getException()); 
     } 
    }); 
    } 
} 

// executes database operations concurrent to JavaFX operations. 
private ExecutorService databaseExecutor = Executors.newFixedThreadPool(
    1, 
    new DatabaseThreadFactory() 
); 

static class DatabaseThreadFactory implements ThreadFactory { 
    static final AtomicInteger poolNumber = new AtomicInteger(1); 

    @Override public Thread newThread(Runnable runnable) { 
    Thread thread = new Thread(runnable, "Database-Connection-" + poolNumber.getAndIncrement() + "-thread"); 
    thread.setDaemon(true); 

    return thread; 
    } 
}  

Lưu ý rằng khi bạn bắt đầu làm việc kiêm nhiệm, mã hóa của bạn và giao diện người dùng của bạn càng ngày càng phức tạp hơn so với chế độ mặc định mà không cần Nhiệm vụ khi mọi thứ đều đơn ren . Ví dụ, trong ví dụ của tôi, tôi vô hiệu hóa nút khởi tạo Task để bạn không thể có nhiều Task đang chạy ở chế độ nền làm việc tương tự (kiểu xử lý này tương tự như thế giới web nơi bạn có thể vô hiệu hóa nút post form để ngăn chặn hình thức được đăng đôi). Tôi cũng đã thêm một chỉ báo tiến trình hoạt hình vào cảnh trong khi nhiệm vụ cơ sở dữ liệu chạy dài đang thực thi để người dùng có chỉ báo rằng có điều gì đó đang diễn ra.

Mẫu chương trình đầu ra chứng minh kinh nghiệm giao diện người dùng khi một hoạt động cơ sở dữ liệu dài chạy là cơ bản dở dang (chú ý chỉ số tiến bộ được hiệu ứng động trong lấy có nghĩa là giao diện người dùng là đáp ứng mặc dù ảnh chụp màn hình không hiển thị này):

databasefetcher

Để so sánh tính phức tạp và chức năng bổ sung của việc triển khai với các tác vụ đồng thời so với triển khai thực hiện mọi thứ trên chuỗi ứng dụng JavaFX, bạn có thể xem another version of the same sample which does not use tasks. Lưu ý rằng trong trường hợp của tôi với một đồ chơi, cơ sở dữ liệu cục bộ phức tạp hơn của ứng dụng dựa trên nhiệm vụ là không cần thiết vì các hoạt động cơ sở dữ liệu cục bộ thực thi quá nhanh, nhưng nếu bạn đang kết nối với một cơ sở dữ liệu từ xa lớn bằng các truy vấn phức tạp chạy dài hơn cách tiếp cận là đáng giá vì nó cung cấp cho người dùng trải nghiệm giao diện người dùng mượt mà hơn.

+0

Cảm ơn bạn. Tôi đã quản lý để sử dụng ví dụ tuyệt vời của bạn để cho phép truy vấn cơ sở dữ liệu của tôi chạy trong nền và sau đó thêm các mục vào dạng xem bảng. Tuy nhiên tôi có một câu hỏi khác. Tại sao khi tôi chạy một truy vấn cơ sở dữ liệu mà thay đổi giá trị của Nhãn (bằng cách sử dụng label.setText (rs.getString (x)), nó từ chối làm việc? Tôi thiếu một cái gì đó đơn giản.Tôi giả sử một bảng hoạt động vì nó chỉ đơn giản là thêm dữ liệu vào Danh sách có thể quan sát mà chế độ xem bảng sau đó sử dụng, nhưng tôi không chắc chắn cách nào để thực hiện chuỗi thay đổi nhãn của tôi? - xem http://pastebin.com/TXyf00xq – Uniqum

3

Được quản lý để giải quyết bằng cách sử dụng giải pháp được cung cấp bởi jewelsea. Cần lưu ý rằng nếu thực hiện phương thức này khi không sử dụng danh sách, bảng và/hoặc danh sách quan sát, nơi bạn cần cập nhật một mục trên giao diện người dùng như trường văn bản hoặc nhãn thì chỉ cần thêm mã cập nhật trong Platform.runLater. Dưới đây là một số đoạn mã hiển thị giải pháp làm việc của tôi.

Code:

public void getSalesData() { 
    try { 
     Connection con = DriverManager.getConnection(connectionUrl); 
     //Get all records from table 
     String SQL = "EXEC dbo.Order_Information"; 
     try (Statement stmt = con.createStatement(); ResultSet rs = 
         stmt.executeQuery(SQL)) { 
      while (rs.next()) { 

       todayTot = Double.parseDouble(rs.getString(7)); 
       weekTot = Double.parseDouble(rs.getString(8)); 
       monthTot = Double.parseDouble(rs.getString(9)); 
       yearTot = Double.parseDouble(rs.getString(10)); 
       yearTar = Double.parseDouble(rs.getString(11)); 
       monthTar = Double.parseDouble(rs.getString(12)); 
       weekTar = Double.parseDouble(rs.getString(13)); 
       todayTar = Double.parseDouble(rs.getString(14)); 
       deltaValue = Double.parseDouble(rs.getString(17)); 

       yearPer = yearTot/yearTar * 100; 
       monthPer = monthTot/monthTar * 100; 
       weekPer = weekTot/weekTar * 100; 
       todayPer = todayTot/todayTar * 100; 

       //Doesn't update UI unless you add the update code to Platform.runLater... 
       Platform.runLater(new Runnable() { 
        public void run() { 
         todayTotal.setText("£" + formatter.format(todayTot)); 
         weekTotal.setText("£" + formatter.format(weekTot)); 
         monthTotal.setText("£" + formatter.format(monthTot)); 
         yearTotal.setText("£" + formatter.format(yearTot)); 
         yearTarget.setText("£" + formatter.format(yearTar)); 
         monthTarget.setText("£" + formatter.format(monthTar)); 
         weekTarget.setText("£" + formatter.format(weekTar)); 
         todayTarget.setText("£" + formatter.format(todayTar)); 
         yearPercent.setText(percentFormatter.format(yearPer) + "%"); 
         currentDelta.setText("Current Delta (Week Ends): £" 
           + formatter.format(deltaValue)); 
        } 
       }); 

      } 
     } 

    } catch (SQLException | NumberFormatException e) { 
    } 
} 


    public void databaseThreadTester() { 
    fetchDataFromDB(); 
} 

private void fetchDataFromDB() { 
    final testController.FetchNamesTask fetchNamesTask = new testController.FetchNamesTask(); 
    databaseActivityIndicator.setVisible(true); 
    databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty()); 
    fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() { 
     @Override 
     public void handle(WorkerStateEvent t) { 
     } 
    }); 
    fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() { 
     @Override 
     public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) { 
      if (!isRunning) { 
       databaseActivityIndicator.setVisible(false); 
      } 
     } 
    ; 
    }); 
databaseExecutor.submit(fetchNamesTask); 
} 

abstract class DBTask<T> extends Task { 

    DBTask() { 
     setOnFailed(new EventHandler<WorkerStateEvent>() { 
      @Override 
      public void handle(WorkerStateEvent t) { 
      } 
     }); 
    } 
} 

class FetchNamesTask extends testController.DBTask { 

    @Override 
    protected String call() throws Exception { 

     fetchNames(); 

     return null; 
    } 

    private void fetchNames() throws SQLException, InterruptedException { 
     Thread.sleep(5000); 
     getTopOrders(); 
     getSalesData(); 
    } 
} 

Điều duy nhất mà không xuất hiện để làm việc với thực hiện đây là những điều sau đây, không chắc chắn lý do tại sao nó không hoạt động nhưng nó không vẽ đồ thị.

public void addCricketGraphData() { 

    yearChart.getData().clear(); 
    series.getData().clear(); 
    series2.getData().clear(); 
    try { 
     Connection con = DriverManager.getConnection(connectionUrl); 
     //Get all records from table 
     String SQL = "...omitted..."; 
     try (Statement stmt = con.createStatement(); ResultSet rs = 
         stmt.executeQuery(SQL)) { 
      while (rs.next()) { 
       Platform.runLater(new Runnable() { 
        @Override 
        public void run() { 
         try { 
          series.getData().add(new XYChart.Data<String, Number>(rs.getString(1), 
            Double.parseDouble(rs.getString(7)))); 
          series2.getData().add(new XYChart.Data<String, Number>(rs.getString(1), 
            Double.parseDouble(rs.getString(8)))); 
         } catch (SQLException ex) { 
          Logger.getLogger(testController.class.getName()).log(Level.SEVERE, null, ex); 
         } 
        } 
       }); 

      } 
     } 

    } catch (SQLException | NumberFormatException e) { 
    } 
    yearChart = createChart(); 
} 

protected LineChart<String, Number> createChart() { 
    final CategoryAxis xAxis = new CategoryAxis(); 
    final NumberAxis yAxis = new NumberAxis(); 

    // setup chart 
    series.setName("Target"); 
    series2.setName("Actual"); 
    xAxis.setLabel("Period"); 
    yAxis.setLabel("£"); 

    //Add custom node for each point of data on the line chart. 
    for (int i = 0; i < series2.getData().size(); i++) { 
     nodeCounter = i; 
     final int value = series.getData().get(nodeCounter).getYValue().intValue(); 
     final int value2 = series2.getData().get(nodeCounter).getYValue().intValue(); 
     int result = value2 - value; 

     Node node = new HoveredThresholdNode(0, result); 
     node.toBack(); 
     series2.getData().get(nodeCounter).setNode(node); 
    } 

    yearChart.getData().add(series); 
    yearChart.getData().add(series2); 

    return yearChart; 
} 
Các vấn đề liên quan