Как построить многоуровневый TreeView в JavaFX с использованием источника данных JDB C? - PullRequest
0 голосов
/ 28 апреля 2020

В моей программе на JavaFX я хочу динамически генерировать дерево, используя живой набор данных из моей базы данных (я использую MariaDB, но это может быть любая база данных SQL).

Я искал Я не смог найти прямой ответ на мой вопрос, поэтому я потратил некоторое время на изучение работы JDB C ResultSet, работы метода next() и работы циклов while. Несколько попыток проб и ошибок наконец привели меня к желаемому результату, поэтому я подумал, что поделюсь им, если кто-то еще окажется на моем месте.

См. Мой ответ ниже.

Ответы [ 2 ]

0 голосов
/ 30 апреля 2020

Вот моя попытка сделать это, не блокируя UI Thread:

public class YourClassName {

ArrayList<ResultBean> fetchedData = null;

public void createTree() {

    // run in other thread fetching the data
    Task task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            // show loading pane -> you can use the one from import org.controlsfx.control.MaskerPane
            // https://github.com/controlsfx/controlsfx/wiki/ControlsFX-Features
            loadingPane.setVisible(true);

            // get data
            fetchedData = generateTreeData(yourDataSource);
            return null;
        }

        @Override
        protected void succeeded() {
            // the data has been fetched, now is safe to build your tree
            super.succeeded();
            buildTreeFromTheData(fetchedData);
        }
    };
    new Thread(task).start();
  }
}

Получить данные из вашей базы данных:

public ArrayList<ResultBean> getTreeData(DataSource dataSource) {

    ArrayList<ResultBean> resultBeans = new ArrayList<>();
    source = null;
    this.source = dataSource;

    Connection con = null;
    Statement stmt = null;

    try {
        con = source.getConnection();
        try {
            stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT " +
                    "sites.siteid, " +
                    "sites.longname, " +
                    "plants.plantid, " +
                    "plants.siteplantid, " +
                    "plants.shortname " +
                    "FROM sites " +
                    "INNER JOIN plants ON plants.siteid=sites.siteid " +
                    "ORDER BY sites.longname ASC, plants.siteplantid ASC");

            while (rs.next()) {

                ResultBean resBean = new ResultBean();
                resBean.setSiteId(rs.getString("sites.siteid"));
                //....
                //....
                // set all values and add it to the result array
                resultBens.add(resBean);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    return resultBens;
}

ResultBean:

public class ResultBean {
    private int siteid;
    private String longname;
    private int plantid;
    private int siteplantid;
    private String shortname;

    // setters
    ....
    // getters
    ....
}

И, наконец, постройте дерево с вашими логиками c:

    public void buildTreeFromTheData(ArrayList<ResultBean> treeData) {

    // do you logic here, loop over treeData ArrayList in while loop
    TreeItem<String> rootTreeItem = new TreeItem<String>("EMT");
    rootTreeItem.setExpanded(true);
    emtTree.setRoot(rootTreeItem);

    TreeItem<String> site = null;
    TreeItem<String> plant = null;

    //This bit prevents repeating the same first level items multiple times.
    //I only want each site to appear once, and under each site is a list
    //of plants.
    //if (!site1.equals(rs.getString("sites.longname"))) {
    //    site = new TreeItem<String>(rs.getString("sites.longname"));
    //    rootTreeItem.getChildren().add(site);
    //    site1 = rs.getString("sites.longname");
   // }

    //This section is never skipped and will add all the plants to a given
    //site until the next site is reached in the result set, then the if
    //is triggered again and the process for the new site.
    // plant = new TreeItem<String>(rs.getInt("plants.siteplantid") + " " + rs.getString("plants.shortname"));
    // site.getChildren().add(plant);

   // finally, hide the loading pane
   maskerPane.setVisible(false);
}
0 голосов
/ 28 апреля 2020

edit Кажется, я плохо спроектировал всю программу, не используя многопоточность для изоляции GUI от запроса JDB C. Я думаю, что это можно исправить с помощью параллелизма JavaFX, но я никогда не использовал его, пока не смогу обновить приведенный ниже код, просто проигнорируйте вещи за пределами while l oop.

Во-первых, я ' используя следующие версии MariaDB (10.4.12), Java (13.0.2.8), JavaFX (13.0.2) и MariaDB JDB C (2.6.0). Я не думаю, что что-то из этого будет иметь значение, но на всякий случай ... Я использую F XML, поэтому вы нигде не увидите там никакого форматирования пользовательского интерфейса.

Это это полный метод, который генерирует TreeView в моей программе JavaFX. Затем он вызывается из отдельного класса вскоре после генерации объектов Stage и Scene.

Их ключевой частью является while l oop, что было для меня проблемой, чтобы получить права. Сначала я думал, что мне нужен вложенный while(rs.next()) l oop, но потом понял, что это приводит к пропуску строк, поскольку каждый вызов rs.next() связан с предыдущим, а не с while l oop в котором он используется.

Обратите также внимание на важность оператора SQL. Если утверждение дает результаты не по порядку, метод не работает правильно.

    public void generateTree(DataSource dataSource) {

        source = null;
        this.source = dataSource;

        Connection con = null;
        Statement stmt = null;

        TreeItem<String> rootTreeItem = new TreeItem<String>("EMT");
        rootTreeItem.setExpanded(true);
        emtTree.setRoot(rootTreeItem);

        TreeItem<String> site = null;
        TreeItem<String> plant = null;

        try {
            con = source.getConnection();
            try {
                stmt = con.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT sites.siteid, sites.longname, plants.plantid, plants.siteplantid, plants.shortname FROM sites INNER JOIN plants ON plants.siteid=sites.siteid ORDER BY sites.longname ASC, plants.siteplantid ASC");

                String site1 = "";  //It's not possible for the site name to be "" in the result set because the database design prevents it.

                //Steps through the result set from first row item to last row item.
                while(rs.next()) {

                    //This bit prevents repeating the same first level items multiple times.
                    //I only want each site to appear once, and under each site is a list
                    //of plants.
                    if (!site1.equals(rs.getString("sites.longname"))) {
                        site = new TreeItem<String>(rs.getString("sites.longname"));
                        rootTreeItem.getChildren().add(site);
                        site1 = rs.getString("sites.longname");
                    }

                        //This section is never skipped and will add all the plants to a given
                        //site until the next site is reached in the result set, then the if
                        //is triggered again and the process for the new site.
                        plant = new TreeItem<String>(rs.getInt("plants.siteplantid") + " " + rs.getString("plants.shortname"));
                        site.getChildren().add(plant);
                }
            } catch (SQLException e) {
                System.out.println("Statement Error");
                System.err.println("SQL State: " + ((SQLException)e).getSQLState());
                System.err.println("Error Code: " + ((SQLException)e).getErrorCode());
                System.err.println("Message: " + ((SQLException)e).getMessage());
                System.err.println("Cause: " + ((SQLException)e).getCause());
                return;
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return;
        } finally {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

Я, вероятно, найду способы улучшить его в будущем, но сейчас я просто счастлив, что он работает. Не обращайте внимания на грязную обработку исключений - работа в процессе!

...