单击另一个表中的元素时更新表

时间:2016-10-19 10:26:50

标签: java javafx tableview

我需要在表上显示元素,具体取决于单击另一个表的元素(Person)。问题是,使用服务,如果用户非常快速地点击第一个表的两个元素,表中会显示两个元素的数据,我只想显示最后一个单击的数据。希望你能帮帮我。

这是我的代码:

personTable.getSelectionModel().selectedItemProperty().addListener(
            (observable, oldValue, newValue) -> {
                try {
                    contactoTable.setPlaceholder(new Label("Cargando..."));
                    showPersonDetails(newValue);
                } catch (SQLException ex) {
                    Logger.getLogger(PersonOverviewController.class.getName()).log(Level.SEVERE, null, ex);
                }
            });

showPersonDatails:

 contactoTable.setVisible(true);
        contactoTable.getItems().clear();

        firstNameLabel.setText(person.getFirstName());
        lastNameLabel.setText(person.getLastName());
        mailLabel.setText(person.getMail());
        phoneLabel.setText(person.getPhone());
        descriptionLabel.setText(person.getDescription());

        service = new Service<Void>() {
            @Override
            protected Task<Void> createTask() {
                return new Task<Void>() {
                    @Override
                    protected Void call() throws Exception {
                        //Background work 
                        DBManager db = new DBManager();
                        String query = "SELECT * FROM eventos";
                        ResultSet r = db.executeSelect(query);
                        contactoTable.getItems().clear();
                        contactoData.clear();

                        while (r.next()) {
                            String q = "SELECT * FROM " + r.getString("Nombre").replace(" ", "_") + " WHERE Nombre = '" + person.getFirstName() + "' AND Apellidos = '" + person.getLastName() + "' AND Correo = '" + person.getMail() + "'";
                            ResultSet result = db.executeSelect(q);

                            while (result.next()) {
                                contactoData.add(new Row(r.getString("Nombre"), result.getString("Asistencia")));
                            }
                        }

                        final CountDownLatch latch = new CountDownLatch(1);
                        Platform.runLater(() -> {
                            try {
                                //FX Stuff done here
                                contactoTable.setPlaceholder(new Label("No invitado a ningún evento"));
                                contactoTable.setItems(contactoData);

                            } finally {
                                latch.countDown();

                            }
                        });
                        latch.await();
                        //Keep with the background work
                        return null;
                    }
                };
            }
        };

        service.start();

1 个答案:

答案 0 :(得分:0)

您正在从多个线程引用相同的数据列表(contactoData),但列表上显然没有同步。如果用户快速连续选择两个不同的项目,则为每个项目启动服务,每个服务在不同的线程中运行其任务。因此,您无法控制两个不同线程在contactoData上执行(多个)操作的顺序。例如,可能(甚至可能)异步执行的两个服务的顺序是:

  1. 第一项服务清除列表
  2. 第二次服务清除列表
  3. 第一项服务将元素添加到列表中
  4. 第二项服务将元素添加到列表中
  5. 在这种情况下,列表包含两个服务生成的元素,而不仅仅是其中一个。

    因此,您应该让您的任务在他们创建的新列表上运行并返回。然后在FX应用程序线程上处理该列表。

    它还不清楚为什么你需要这里的服务,因为你似乎只使用过一次服务。您也可以直接使用任务。

    您可能还希望确保最后一个选择是显示的选项。由于任务是异步运行的,因此如果两个任务快速连续启动,则第二个任务可能在第一个任务之前完成。这将导致显示第二个选择,然后第一个选择替换它。您可以通过在onSucceeded处理程序中执行UI更新来避免这种情况,并在启动新任务时取消任何当前任务(从而阻止当前正在执行的任务调用其onSucceeded处理程序)。

    最后,我真的不清楚为什么要让任务等到UI更新为止。

    以下是您的代码的更新版本:

    private Task<List<Row>> updateContactTableTask ;
    
    // ...
    
    private void showPersonDetails(Person person) {
    
        contactoTable.getItems().clear();
    
        firstNameLabel.setText(person.getFirstName());
        lastNameLabel.setText(person.getLastName());
        mailLabel.setText(person.getMail());
        phoneLabel.setText(person.getPhone());
        descriptionLabel.setText(person.getDescription());
    
    
        if (updateContactTableTask != null && updateContactTableTask.isRunning()) {
            updateContactTableTask.cancel();
        }
    
        updateContactTableTask = new Task<List<Row>>() {
            @Override
            protected List<Row> call() throws Exception {
    
                List<Row> resultList = new ArrayList<>() ;
    
                //Background work 
                DBManager db = new DBManager();
                String query = "SELECT * FROM eventos";
                ResultSet r = db.executeSelect(query);
    
                // quit if we got canceled here...
                if (isCancelled()) {
                    return resultList;
                }
    
                while (r.next() && ! isCancelled()) {
    
                    // Note: building a query like this is inherently unsafe
                    // You should use a PreparedStatement in your DBManager class instead
                    String q = "SELECT * FROM " + r.getString("Nombre").replace(" ", "_") + " WHERE Nombre = '" + person.getFirstName() + "' AND Apellidos = '" + person.getLastName() + "' AND Correo = '" + person.getMail() + "'";
                    ResultSet result = db.executeSelect(q);
    
                    while (result.next()) {
                        resultList.add(new Row(r.getString("Nombre"), result.getString("Asistencia")));
                    }
    
                }
    
                return resultList ;
            }
        };
    
        updateContactTableTask.setOnSucceeded(e -> {
            // not really clear you still need contactoData, but if you do:
            contactoData.setAll(updateContactTableTask.getValue());
            contactoTable.setPlaceholder(new Label("No invitado a ningún evento"));
            contactoTable.setItems(contactoData);
        });
    
        updateContactTableTask.setOnFailed(e -> {
            // handle database errors here...
        });
    
        new Thread(updateContactTableTask).start();
    }
    

    顺便说一句,我不清楚是否,如果是这样,你是如何关闭数据库资源的。例如。结果集似乎永远不会被关闭。这可能会导致资源泄漏。然而,这是问题的附带问题(并且依赖于了解您的DBManager课程的实施方式),所以我不会在这里解决它。