我正在尝试创建一个带有侦听器的搜索栏,以执行实时搜索和过滤javafx
. dataSearch
当我单击启动程序的执行按钮时,应该触发该方法。最初,我的搜索栏似乎工作正常,但问题是,当我从应用程序更新 Oracle 数据库时,TableView
搜索栏停止对输入关键字进行过滤。看起来该dataSearch
方法每次执行程序时只调用一次,并且TableView
修改后,ObservableList
被调用的方法generalList
不会自动更新。
代码示例:
public class App extends Application {
private TableView<General> generalTableView;
private ObservableList<General> generalList;
private TextField searchBox;
private TextField addTextField;
@Override
public void start(Stage primaryStage) {
generalTableView = new TableView<>();
TableColumn<General, String> generalColumn = new TableColumn<>("Col");
generalColumn.setCellValueFactory(new PropertyValueFactory<>("col"));
generalTableView.getColumns().add(generalColumn);
Button loadDataButton = new Button("Load Data");
loadDataButton.setOnAction(event -> {
createGeneralTable();
loadGeneralData();
dataSearch();
});
searchBox = new TextField();
searchBox.setPromptText("Search");
addTextField = new TextField();
addTextField.setPromptText("Add Data");
Button addButton = new Button("Add");
addButton.setOnAction(event -> addData(addTextField.getText()));
HBox searchBoxContainer = new HBox(searchBox);
HBox addBoxContainer = new HBox(addTextField, addButton);
VBox root = new VBox(generalTableView, loadDataButton, searchBoxContainer, addBoxContainer);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
loadDataButton.fire(); // Trigger the button action when the program starts
}
public static void main(String[] args) {
launch(args);
}
static class General {
private ObjectProperty<String> col;
public General(String col) {
this.col = new SimpleObjectProperty<>(col);
}
public String getCol() {
return col.get();
}
public void setCol(String col) {
this.col.set(col);
}
public ObjectProperty<String> colProperty() {
return col;
}
}
static class OracleConnect {
public static Connection getConnection() {
Connection connection = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "system", "o4a75e");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
private void createGeneralTable() {
try {
Connection connection = OracleConnect.getConnection();
String createTableGeneral = "CREATE TABLE general (col VARCHAR2(50))";
PreparedStatement statement = connection.prepareStatement(createTableGeneral);
statement.execute();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void loadGeneralData() {
try {
Connection connection = OracleConnect.getConnection();
String query = "SELECT col FROM general";
PreparedStatement statement = connection.prepareStatement(query);
ResultSet resultSet = statement.executeQuery();
generalList = FXCollections.observableArrayList();
while (resultSet.next()) {
String col = resultSet.getString("col");
General general = new General(col);
generalList.add(general);
}
generalTableView.setItems(generalList);
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private void dataSearch() {
ObservableList<General> generalList = FXCollections.observableArrayList(generalTableView.getItems());
FilteredList<General> filteredData = new FilteredList<>(generalList, b -> true);
searchBox.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(general -> {
if (newValue.isEmpty() || newValue.isBlank() || newValue == null) {
return true;
}
String searchKeyword = newValue.toLowerCase();
if (general.getCol().toLowerCase().contains(searchKeyword)) {
return true;
} else {
return false;
}
});
});
SortedList<General> sortedData = new SortedList<>(filteredData);
sortedData.comparatorProperty().bind(generalTableView.comparatorProperty());
generalTableView.setItems(sortedData);
}
private void addData(String data) {
try {
Connection connection = OracleConnect.getConnection();
String insertQuery ="INSERT INTO general (col) VALUES (?)";
PreparedStatement statement = connection.prepareStatement(insertQuery);
statement.setString(1, data);
statement.executeUpdate();
General general = new General(data);
generalList.add(general);
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
此答案用于过滤添加到支持 a 的内存列表中的数据
TableView
,因此不包含数据库代码。您问题代码中的主要问题是您不必要地创建新的数据列表。相反,只需拥有一个数据列表并在任何地方使用它,将其包装在单个 FilteredList 和单个 SortedList 中,以便对基础列表数据进行所需的转换。
强制不创建超出需要的对象和集合实例的一个好方法是将引用声明为 Final,这是示例代码中演示的技术。
示例:排序、过滤的 TableView
猫.java
创建一个记录来保存您的数据。
IronCatFistClan.java
创建一个模型来保存您的记录,并对它们进行排序和过滤。
您可以简单地调用它
Model
而不是IronCatFistClan
.FilteredCatApp.java
创建一个与模型交互的 UI,显示数据的表格视图,并使用对数据设置过滤器的搜索函数和向数据添加新记录的添加函数。
常问问题
不,我不建议这样做。
也许您可以创建一个数据库触发器来监视数据更改。触发器可以调用 Java 代码(我不知道如何执行)来通知数据库中的数据已更改。Java代码可以查询数据库中的新数据并将更改事件通知给侦听器。然而,这似乎是一种xy 问题。
支持 a 的数据
TableView
是数据库中数据的内存副本。如果并发数据库更新,它可能会不同步。保持数据同步的一种方法是让数据库通知并将更改推送到应用程序(如上面概述的触发器概念)。然而,更常见的方法是每次想要根据新数据更新 UI 时从数据库中检索当前数据。应用程序在需要时从数据库中提取当前数据,而不是尝试让数据库发布数据已更改的某些事件。
不。您的方法
dataSearch()
是从您的方法调用loadGeneralData
的,该方法在onAction
您的loadDataButton
. 您可以fire
在应用程序启动时使用该按钮,但如果用户手动按下该按钮,也可以执行该方法。每次调用时,
dataSearch()
您都会向搜索文本添加一个新的侦听器。你可能不想这样做。每次按下时,loadDataButton
都会添加新的侦听器,而旧的侦听器不会被删除,并且它们最终将保留对旧数据列表的引用,这是一种错误。您至少应该在添加新侦听器之前删除旧侦听器以保留该方法。更好的解决方案是一种 MVC,您可以在其中与可观察的数据模型进行交互,更新模型中的数据等方面,或者根据需要进行过滤和排序(如本答案中的示例所示)。
如果需要(此处未显示),可以将事务持久性服务和数据访问对象添加到应用程序中,以便根据需要将模型代码与数据库连接起来。