我需要在表上显示元素,具体取决于单击另一个表的元素(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();
答案 0 :(得分:0)
您正在从多个线程引用相同的数据列表(contactoData
),但列表上显然没有同步。如果用户快速连续选择两个不同的项目,则为每个项目启动服务,每个服务在不同的线程中运行其任务。因此,您无法控制两个不同线程在contactoData
上执行(多个)操作的顺序。例如,可能(甚至可能)异步执行的两个服务的顺序是:
在这种情况下,列表包含两个服务生成的元素,而不仅仅是其中一个。
因此,您应该让您的任务在他们创建的新列表上运行并返回。然后在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
课程的实施方式),所以我不会在这里解决它。