如何从用户选择的数据库在 JavaFX 中创建动态 TableView table
How to create a dynamic TableView in JavaFX from a user selected database table
让我澄清一下这个问题。
我正在创建一个具有 2 个组合框的 JavaFX 应用程序。一个显示 MySQL DB 中的可用目录,另一个显示第一个 ComboBox 中选定目录中的可用表。现在的问题是我需要创建一个 TableView 来显示 "desc tableName" MySQL 命令的结果。
我正在使用 jdbc API,使用语句接口执行“desc ${tableName}
”并将数据放入结果集中。我能够使用 ResultSetMetaData 对象中的列名填充 TableView 列名。
现在,我知道为了填充 TableView 中的数据单元格,我们需要使用定义数据模型的 pojo class。但是,现在你应该明白了,因为表格是由用户动态选择的,所以我至少不能为 TableView 创建一个通用数据模型 class。
我也知道我们不能以编程方式在 TableView 的单个单元格中设置数据。
因此,我改为使用 HashMap 作为 TableView 的数据模型,并将 HashMap 的键用作 CellValueFactory 属性。
TableView<HashMap<String, String>> tableView;
ObservableList<TableColumn<HashMap<String, String>, String>> tvcolumns = FXCollections.observableArrayList();
tableColumns.forEach((t) -> {
tvcolumns.add(new TableColumn<>(t));
});
for (int i = 0; i < tvcolumns.size(); i++) {
TableColumn<HashMap<String, String>, String> temp = tvcolumns.get(i);
temp.setCellValueFactory(new PropertyValueFactory<>(keySet.get(i)));
}
tableView.getItems().clear();
tableView.setItems(tableData);
tableView.getColumns().addAll(tvcolumns);
现在这个HashMaps的key包含了ResultSet数据的列名,values包含了ResultSet中对应列的数据。我以这样一种方式编写代码,即 TableView 接收这些 HashMap 对象的 ObservableList<>(ResultSet 中的每一行对应一个 HashMap 对象),并根据 HashMap 的键用 HashMap 的值填充 TableView 中的单元格。
但是数据没有显示在 TableView 中。我什至不知道这样的事情是否可能。
完整代码如下:
--> MainUiController.java :
public class MainUiController implements Initializable {
@FXML
private AnchorPane rootPane;
@FXML
private VBox vBoxMain;
@FXML
private HBox hBoxTop;
@FXML
private VBox vBoxTopLeft;
@FXML
private ComboBox<String> cbCatalog;
@FXML
private VBox vBoxTopRight;
@FXML
private ComboBox<String> cbTables;
@FXML
private HBox hBoxBottom;
@FXML
private TitledPane titledPaneBottom;
@FXML
private AnchorPane anchorPaneBottom;
@FXML
private TableView<HashMap<String, String>> tableView;
DBHelper dbHelper = new DBHelper();
/**
* Initializes the controller class.
*
* @param url
* @param rb
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
ObservableList<String> catalogItems = cbCatalog.getItems();
catalogItems.clear();
catalogItems.addAll(dbHelper.getCatalogs());
titledPaneBottom.setExpanded(false);
}
@FXML
private void populateTables(ActionEvent event) {
String dbName = ((ComboBox<String>) event.getSource()).getValue();
System.out.println("catalog value: " + dbName);
dbHelper.useDB(dbName);
ObservableList<String> tables = cbTables.getItems();
tables.clear();
tables.addAll(dbHelper.getTables());
}
@FXML
private void descTable(ActionEvent event) {
String tableName = ((ComboBox<String>) event.getSource()).getValue();
ObservableList<String> tableColumns = dbHelper.getTableColumns(tableName);
ObservableList<HashMap<String, String>> tableData = dbHelper.getTableData();
List<String> keySet = null;
if (!tableData.isEmpty()) {
keySet = new ArrayList<>(tableData.get(0).keySet());
}
titledPaneBottom.setText("\"" + tableName + "\" description:");
ObservableList<TableColumn<HashMap<String, String>, String>> tvcolumns = FXCollections.observableArrayList();
tableColumns.forEach((t) -> {
tvcolumns.add(new TableColumn<>(t));
});
for (int i = 0; i < tvcolumns.size(); i++) {
TableColumn<HashMap<String, String>, String> temp = tvcolumns.get(i);
temp.setCellValueFactory(new PropertyValueFactory<>(keySet.get(i)));
}
tableView.getItems().clear();
tableView.setItems(tableData);
tableView.getColumns().addAll(tvcolumns);
}
}
--> DBHelper.java:
class DBHelper {
Connection con;
Statement st;
ObservableList<String> tablesList = FXCollections.observableArrayList();
ObservableList<String> columnList = FXCollections.observableArrayList();
HashMap<String, String> tableData;
ObservableList<HashMap<String, String>> tableDataList = FXCollections.observableArrayList();
String dbName, tableName;
ResultSet tableCols;
ResultSetMetaData colsMetaData;
public DBHelper() {
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/", "*****", "******");
st = con.createStatement();
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public ObservableList<String> getCatalogs() {
try {
ObservableList<String> catalogList = FXCollections.observableArrayList();
DatabaseMetaData dbmd = con.getMetaData();
ResultSet catalogs = dbmd.getCatalogs();
while (catalogs.next()) {
catalogList.add(catalogs.getString(1));
}
return catalogList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public ObservableList<String> getTables() {
try {
ResultSet tables = st.executeQuery("show tables");
tablesList.clear();
while (tables.next()) {
tablesList.add(tables.getString(1));
}
return tablesList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
void useDB(String dbName) {
this.dbName = dbName;
try {
int execute = st.executeUpdate("use " + dbName);
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public ObservableList<String> getTableColumns(String tableName) {
this.tableName = tableName;
try {
tableCols = st.executeQuery("desc " + tableName);
colsMetaData = tableCols.getMetaData();
columnList.clear();
int count = 1;
while (count <= colsMetaData.getColumnCount()) {
columnList.add(colsMetaData.getColumnName(count));
count++;
}
return columnList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public ObservableList<HashMap<String, String>> getTableData() {
tableDataList.clear();
if (tableCols != null & colsMetaData != null) {
try {
while (tableCols.next()) {
tableData = new HashMap<>();
int count = 1;
while (count <= colsMetaData.getColumnCount()) {
tableData.put(colsMetaData.getColumnName(count), tableCols.getString(count));
count++;
}
tableDataList.add(tableData);
}
tableCols.close();
tableCols = null;
colsMetaData = null;
return tableDataList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
return null;
}
}
请帮我解决这个问题。我很确定使用 HashMap 方法作为 TableView 的数据模型是 wrong/not 可能的。但我不知道在这种情况下如何创建通用数据模型对象。
请帮忙解决这个问题。
我目前正在学习 JavaFx,我想让这个应用程序成为一个挑战。对此的任何见解都将对我的职业生涯大有帮助。
这可能会有帮助。我使用接受 ResultSet
作为数据的自定义 TableView
。
它的实现有点乱,但在这些情况下对我有用。我不确定在数据源之外保持 ResultSet
打开是一个明智的决定,但它至少可以引导您朝着正确的方向前进:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.util.Callback;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class ResultSetTableView extends TableView {
private ResultSet resultSet;
private List<String> columnNames = new ArrayList<>();
public ResultSetTableView(ResultSet resultSet) throws SQLException {
super();
this.resultSet = resultSet;
buildData();
}
private void buildData() throws SQLException {
ObservableList<ObservableList> data = FXCollections.observableArrayList();
for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
final int j = i;
TableColumn col = new TableColumn(resultSet.getMetaData().getColumnName(i + 1));
col.setCellValueFactory((Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>) param -> {
if (param.getValue().get(j) != null) {
return new SimpleStringProperty(param.getValue().get(j).toString());
} else {
return null;
}
});
getColumns().addAll(col);
this.columnNames.add(col.getText());
}
while (resultSet.next()) {
//Iterate Row
ObservableList<String> row = FXCollections.observableArrayList();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
//Iterate Column
row.add(resultSet.getString(i));
}
data.add(row);
}
//FINALLY ADDED TO TableView
setItems(data);
}
public List<String> getColumnNames() {
return columnNames;
}
}
My library 方法签名:
TableViewUtils.createColumnsAndFillTableView(
TableView<List<Object>> tableView,
ResultSet resultSet,
boolean calculatePrefHeight,
StringProperty filterProperty)
作为 filterProperty,您可以传递 TextField textProperty 以根据用户输入过滤项目
让我澄清一下这个问题。
我正在创建一个具有 2 个组合框的 JavaFX 应用程序。一个显示 MySQL DB 中的可用目录,另一个显示第一个 ComboBox 中选定目录中的可用表。现在的问题是我需要创建一个 TableView 来显示 "desc tableName" MySQL 命令的结果。
我正在使用 jdbc API,使用语句接口执行“desc ${tableName}
”并将数据放入结果集中。我能够使用 ResultSetMetaData 对象中的列名填充 TableView 列名。
现在,我知道为了填充 TableView 中的数据单元格,我们需要使用定义数据模型的 pojo class。但是,现在你应该明白了,因为表格是由用户动态选择的,所以我至少不能为 TableView 创建一个通用数据模型 class。
我也知道我们不能以编程方式在 TableView 的单个单元格中设置数据。
因此,我改为使用 HashMap 作为 TableView 的数据模型,并将 HashMap 的键用作 CellValueFactory 属性。
TableView<HashMap<String, String>> tableView;
ObservableList<TableColumn<HashMap<String, String>, String>> tvcolumns = FXCollections.observableArrayList();
tableColumns.forEach((t) -> {
tvcolumns.add(new TableColumn<>(t));
});
for (int i = 0; i < tvcolumns.size(); i++) {
TableColumn<HashMap<String, String>, String> temp = tvcolumns.get(i);
temp.setCellValueFactory(new PropertyValueFactory<>(keySet.get(i)));
}
tableView.getItems().clear();
tableView.setItems(tableData);
tableView.getColumns().addAll(tvcolumns);
现在这个HashMaps的key包含了ResultSet数据的列名,values包含了ResultSet中对应列的数据。我以这样一种方式编写代码,即 TableView 接收这些 HashMap 对象的 ObservableList<>(ResultSet 中的每一行对应一个 HashMap 对象),并根据 HashMap 的键用 HashMap 的值填充 TableView 中的单元格。
但是数据没有显示在 TableView 中。我什至不知道这样的事情是否可能。
完整代码如下:
--> MainUiController.java :
public class MainUiController implements Initializable {
@FXML
private AnchorPane rootPane;
@FXML
private VBox vBoxMain;
@FXML
private HBox hBoxTop;
@FXML
private VBox vBoxTopLeft;
@FXML
private ComboBox<String> cbCatalog;
@FXML
private VBox vBoxTopRight;
@FXML
private ComboBox<String> cbTables;
@FXML
private HBox hBoxBottom;
@FXML
private TitledPane titledPaneBottom;
@FXML
private AnchorPane anchorPaneBottom;
@FXML
private TableView<HashMap<String, String>> tableView;
DBHelper dbHelper = new DBHelper();
/**
* Initializes the controller class.
*
* @param url
* @param rb
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
ObservableList<String> catalogItems = cbCatalog.getItems();
catalogItems.clear();
catalogItems.addAll(dbHelper.getCatalogs());
titledPaneBottom.setExpanded(false);
}
@FXML
private void populateTables(ActionEvent event) {
String dbName = ((ComboBox<String>) event.getSource()).getValue();
System.out.println("catalog value: " + dbName);
dbHelper.useDB(dbName);
ObservableList<String> tables = cbTables.getItems();
tables.clear();
tables.addAll(dbHelper.getTables());
}
@FXML
private void descTable(ActionEvent event) {
String tableName = ((ComboBox<String>) event.getSource()).getValue();
ObservableList<String> tableColumns = dbHelper.getTableColumns(tableName);
ObservableList<HashMap<String, String>> tableData = dbHelper.getTableData();
List<String> keySet = null;
if (!tableData.isEmpty()) {
keySet = new ArrayList<>(tableData.get(0).keySet());
}
titledPaneBottom.setText("\"" + tableName + "\" description:");
ObservableList<TableColumn<HashMap<String, String>, String>> tvcolumns = FXCollections.observableArrayList();
tableColumns.forEach((t) -> {
tvcolumns.add(new TableColumn<>(t));
});
for (int i = 0; i < tvcolumns.size(); i++) {
TableColumn<HashMap<String, String>, String> temp = tvcolumns.get(i);
temp.setCellValueFactory(new PropertyValueFactory<>(keySet.get(i)));
}
tableView.getItems().clear();
tableView.setItems(tableData);
tableView.getColumns().addAll(tvcolumns);
}
}
--> DBHelper.java:
class DBHelper {
Connection con;
Statement st;
ObservableList<String> tablesList = FXCollections.observableArrayList();
ObservableList<String> columnList = FXCollections.observableArrayList();
HashMap<String, String> tableData;
ObservableList<HashMap<String, String>> tableDataList = FXCollections.observableArrayList();
String dbName, tableName;
ResultSet tableCols;
ResultSetMetaData colsMetaData;
public DBHelper() {
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/", "*****", "******");
st = con.createStatement();
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public ObservableList<String> getCatalogs() {
try {
ObservableList<String> catalogList = FXCollections.observableArrayList();
DatabaseMetaData dbmd = con.getMetaData();
ResultSet catalogs = dbmd.getCatalogs();
while (catalogs.next()) {
catalogList.add(catalogs.getString(1));
}
return catalogList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public ObservableList<String> getTables() {
try {
ResultSet tables = st.executeQuery("show tables");
tablesList.clear();
while (tables.next()) {
tablesList.add(tables.getString(1));
}
return tablesList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
void useDB(String dbName) {
this.dbName = dbName;
try {
int execute = st.executeUpdate("use " + dbName);
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
public ObservableList<String> getTableColumns(String tableName) {
this.tableName = tableName;
try {
tableCols = st.executeQuery("desc " + tableName);
colsMetaData = tableCols.getMetaData();
columnList.clear();
int count = 1;
while (count <= colsMetaData.getColumnCount()) {
columnList.add(colsMetaData.getColumnName(count));
count++;
}
return columnList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public ObservableList<HashMap<String, String>> getTableData() {
tableDataList.clear();
if (tableCols != null & colsMetaData != null) {
try {
while (tableCols.next()) {
tableData = new HashMap<>();
int count = 1;
while (count <= colsMetaData.getColumnCount()) {
tableData.put(colsMetaData.getColumnName(count), tableCols.getString(count));
count++;
}
tableDataList.add(tableData);
}
tableCols.close();
tableCols = null;
colsMetaData = null;
return tableDataList;
} catch (SQLException ex) {
Logger.getLogger(DBHelper.class.getName()).log(Level.SEVERE, null, ex);
}
}
return null;
}
}
请帮我解决这个问题。我很确定使用 HashMap 方法作为 TableView 的数据模型是 wrong/not 可能的。但我不知道在这种情况下如何创建通用数据模型对象。 请帮忙解决这个问题。
我目前正在学习 JavaFx,我想让这个应用程序成为一个挑战。对此的任何见解都将对我的职业生涯大有帮助。
这可能会有帮助。我使用接受 ResultSet
作为数据的自定义 TableView
。
它的实现有点乱,但在这些情况下对我有用。我不确定在数据源之外保持 ResultSet
打开是一个明智的决定,但它至少可以引导您朝着正确的方向前进:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.util.Callback;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class ResultSetTableView extends TableView {
private ResultSet resultSet;
private List<String> columnNames = new ArrayList<>();
public ResultSetTableView(ResultSet resultSet) throws SQLException {
super();
this.resultSet = resultSet;
buildData();
}
private void buildData() throws SQLException {
ObservableList<ObservableList> data = FXCollections.observableArrayList();
for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
final int j = i;
TableColumn col = new TableColumn(resultSet.getMetaData().getColumnName(i + 1));
col.setCellValueFactory((Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>) param -> {
if (param.getValue().get(j) != null) {
return new SimpleStringProperty(param.getValue().get(j).toString());
} else {
return null;
}
});
getColumns().addAll(col);
this.columnNames.add(col.getText());
}
while (resultSet.next()) {
//Iterate Row
ObservableList<String> row = FXCollections.observableArrayList();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
//Iterate Column
row.add(resultSet.getString(i));
}
data.add(row);
}
//FINALLY ADDED TO TableView
setItems(data);
}
public List<String> getColumnNames() {
return columnNames;
}
}
My library 方法签名:
TableViewUtils.createColumnsAndFillTableView(
TableView<List<Object>> tableView,
ResultSet resultSet,
boolean calculatePrefHeight,
StringProperty filterProperty)
作为 filterProperty,您可以传递 TextField textProperty 以根据用户输入过滤项目