2013-02-14 14 views
10

के लिए पृष्ठभूमि थ्रेड मैं सोच रहा हूं कि जावाफैक्स में पृष्ठभूमि थ्रेड बनाने के संबंध में कोई भी मुझे परेशान करने वाली समस्या के साथ मेरी मदद कर सकता है! मेरे पास वर्तमान में कई SQL क्वेरी हैं जो यूआई में डेटा जोड़ती हैं जो वर्तमान में जावाएफएक्स एप्लिकेशन थ्रेड पर चलती है (नीचे उदाहरण देखें)। हालांकि जब इन प्रश्नों में से प्रत्येक निष्पादित करता है तो यह यूआई को फ्रीज करता है क्योंकि यह पृष्ठभूमि थ्रेड पर नहीं चल रहा है। मैंने विभिन्न उदाहरणों को देखा है जो कार्य का उपयोग करते हैं और उन्हें समझते हैं लेकिन डेटाबेस क्वेरी करते समय मैं उन्हें काम नहीं कर सकता, जिनमें से कुछ को चलाने के लिए कुछ सेकंड लगते हैं।जावाएफएक्स - एसक्यूएल क्वेरी

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) { 
    } 
} 

प्रत्येक संसाधित रिकॉर्ड एक ObservableList जो एक tableview, या ग्राफ से जुड़ा हुआ या बस एक लेबल पर पाठ सेट है में जोड़ा जाता है (पर निर्भर करता है:

यहाँ कि एक प्रश्न निष्पादित करता है तरीकों में से एक है पूछताछ)। कैसे मैं एक पृष्ठभूमि धागे पर क्वेरी निष्पादित कर सकते हैं और के रूप में अलेक्जेंडर कीरॉफ़ की टिप्पणी में सुझाव अभी भी इंटरफ़ेस पहले से उपयोग करने के लिए और

धन्यवाद प्रश्नों से अद्यतन किया जा

+1

क्या आपने javafx.concurrent पैकेज में कक्षाएं देखी हैं? docs.oracle.com/javafx/2/api/javafx/concurrent/...। मुझे लगता है कि, एक कार्य वर्ग विशेष रूप से आपके लिए दिलचस्प होना चाहिए: docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html, आप विकल्पों की पूरी सूची को समझने के लिए, अपने जावाडोक को पढ़ सकते हैं। –

+0

शायद डेटाएफएक्स आपकी मदद कर सकता है: http://www.javafxdata.org या http://www.guigarage.com/category/datafx/ –

उत्तर

14

मैं एक sample solution बनाई गई एक Task का उपयोग कर (के लिए मुफ्त छोड़) JavaFX अनुप्रयोग थ्रेड पर समवर्ती रूप से निष्पादित थ्रेड पर डेटाबेस तक पहुंचने के लिए।

नमूना समाधान के प्रासंगिक भागों नीचे reproduced रहे हैं:

// 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; 
    } 
}  

ध्यान दें कि एक बार आप अपने कोडिंग समवर्ती काम करने शुरू करते हैं, और अपने यूआई कार्य बिना डिफ़ॉल्ट मोड की तुलना में अधिक जटिल हो जाता है जब सब कुछ एक लड़ी है । उदाहरण के लिए, मेरे नमूने में मैंने उस बटन को अक्षम कर दिया जो कार्य शुरू करता है ताकि आपके पास पृष्ठभूमि में एक ही काम करने वाले कई कार्य नहीं हो सकें (इस प्रकार की प्रसंस्करण वेब दुनिया के समान है जहां आप एक फॉर्म पोस्ट बटन को अक्षम कर सकते हैं ताकि इसे रोकने के लिए फॉर्म डबल पोस्ट किया जा रहा है)। मैंने दृश्य में एक एनिमेटेड प्रगति सूचक भी जोड़ा, जबकि लंबे समय तक चलने वाला डेटाबेस कार्य निष्पादित कर रहा था ताकि उपयोगकर्ता को संकेत हो कि कुछ चल रहा है।

नमूना कार्यक्रम उत्पादन यूआई अनुभव जब एक लंबे चल डेटाबेस कार्रवाई जारी है प्रदर्शन (ध्यान दें प्रगति संकेतक लाने जिसका अर्थ है, हालांकि स्क्रीनशॉट इस प्रदर्शित नहीं करता है यूआई उत्तरदायी है दौरान एनिमेट है):

databasefetcher

समवर्ती कार्यों के साथ कार्यान्वयन की अतिरिक्त जटिलता और कार्यक्षमता की तुलना करने के लिए जावाफैक्स एप्लिकेशन थ्रेड पर सब कुछ निष्पादित करता है, तो आप another version of the same sample which does not use tasks देख सकते हैं। ध्यान दें कि मेरे मामले में खिलौने के साथ, स्थानीय डेटाबेस कार्य आधारित अनुप्रयोग की अतिरिक्त जटिलता अनावश्यक है क्योंकि स्थानीय डेटाबेस संचालन इतनी जल्दी निष्पादित होते हैं, लेकिन यदि आप लंबे समय तक चलने वाले जटिल प्रश्नों का उपयोग कर बड़े रिमोट डेटाबेस से कनेक्ट होते हैं, तो कार्य आधारित दृष्टिकोण सार्थक है क्योंकि यह उपयोगकर्ताओं को एक आसान यूआई अनुभव प्रदान करता है।

+0

धन्यवाद। मैंने पृष्ठभूमि में चलाने के लिए अपनी डेटाबेस क्वेरी को अनुमति देने के लिए अपने उत्कृष्ट उदाहरण का उपयोग करने में कामयाब रहा है और फिर आइटम को तालिका दृश्य में जोड़ दिया है। हालांकि मेरे पास एक और सवाल है। जब मैं डेटाबेस क्वेरी चलाता हूं जो लेबल के मानों को बदलता है (लेबल.सेटटेक्स्ट (rs.getString (x)) का उपयोग करके; क्या यह काम करने से इंकार कर देता है? क्या मुझे कुछ सरल याद आ रहा है। मुझे लगता है कि तालिका एक काम करती है क्योंकि यह बस जोड़ रही है एक अवलोकन सूची में डेटा जो तालिका दृश्य का उपयोग करता है, लेकिन मुझे यकीन नहीं है कि थ्रेड को मेरे लेबल बदलने के लिए किस तरह से जाना है? - http://pastebin.com/TXyf00xq देखें – Uniqum

3

गहने द्वारा प्रदान किए गए समाधान का उपयोग करने के लिए प्रबंधित करने के लिए प्रबंधित किया गया। यह ध्यान देने योग्य है कि यदि इस विधि को लागू करते समय सूचियों, तालिकाओं और/या अवलोकन योग्य सूचियों का उपयोग नहीं करते हैं, जहां आपको UI पर किसी आइटम को टेक्स्ट फ़ील्ड या लेबल जैसे अद्यतन करने की आवश्यकता है तो प्लेटफ़ॉर्म.रुनलाटर के भीतर बस अपडेट कोड जोड़ें। नीचे कुछ कोड स्निपेट हैं जो मेरे कामकाजी समाधान दिखाते हैं।

कोड:

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(); 
    } 
} 

केवल बात यह है कि इस कार्यान्वयन के साथ काम करने के लिए प्रकट नहीं होता है निम्नलिखित, यकीन है कि क्यों यह काम नहीं करता है, लेकिन यह ग्राफ आकर्षित नहीं करता नहीं है।

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; 
} 
संबंधित मुद्दे