Как создать динамический TableView в JavaFX из выбранной пользователем таблицы базы данных - PullRequest
0 голосов
/ 14 января 2019

Позвольте мне прояснить вопрос.

Я создаю приложение JavaFX, которое имеет 2 ComboBox. Один отображает доступные каталоги в MySQL DB, а другой показывает доступные таблицы в выбранном каталоге из первого ComboBox. Теперь проблема в том, что мне нужно создать TableView, который будет отображать результаты команды MySQL "desc tableName".

Я использую jdbc API, выполняю "desc ${tableName}" с помощью интерфейса Statement и получаю данные в ResultSet. Я могу заполнить имена столбцов TableView именами столбцов из объекта ResultSetMetaData.

Теперь я знаю, что для заполнения ячеек данных в TableView нам нужно использовать класс pojo, который будет определять модель данных. Но теперь вы можете понять, что, поскольку таблицы выбираются пользователем динамически, я не могу создать хотя бы общий класс модели данных для TableView.

Я также знаю, что мы не можем программно устанавливать данные в отдельных ячейках 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 содержат имена столбцов данных ResultSet, а значения содержат данные соответствующих столбцов в ResultSet. Я написал код таким образом, что TableView принимает ObservableList <> этих объектов HashMap (один объект HashMap для каждой строки в ResultSet) и заполняет ячейки в TableView значениями HashMap на основе ключей HashMap.

Но данные не отображаются в 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 является неправильным / невозможным. Но я не знаю, как создать универсальный объект datamodel в этом случае. Пожалуйста, помогите решить эту проблему.

В настоящее время я изучаю JavaFx, и я хотел сделать это приложение непростым. Любое понимание этого было бы очень полезно для моей карьеры.

Ответы [ 2 ]

0 голосов
/ 01 июля 2019

Моя библиотека подпись метода:

TableViewUtils.createColumnsAndFillTableView(
               TableView<List<Object>> tableView, 
               ResultSet resultSet,
               boolean calculatePrefHeight, 
               StringProperty filterProperty)

как filterProperty, вы можете передать TextField textProperty для фильтрации элементов по пользовательскому вводу

0 голосов
/ 14 января 2019

Это может быть полезно. Я использую пользовательский TableView, который принимает ResultSet для данных.

Это немного хакерская реализация, но она работает для меня в этих случаях. Я не уверен, что сохранение 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;
    }
}
...