繁体   English   中英

如何使用 JDBC 数据源在 JavaFX 中构建多级 TreeView?

[英]How do I build a multilevel TreeView in JavaFX using a JDBC datasource?

在我的 JavaFX 程序中,我想使用数据库中的实时数据集动态生成一棵树(我使用的是 MariaDB,但它可以是任何 SQL 数据库)。

我搜索了一堆,找不到我的问题的直接答案,所以我花了一些时间学习 JDBC ResultSet的工作原理、 next()方法的工作原理以及while循环的工作原理。 几次反复试验最终使我得到了我想要的结果,所以我想我会分享它,以防其他人发现自己在我的 position 中。

请看下面我的回答。

编辑似乎我在不使用线程将 GUI 与 JDBC 查询隔离时设计的整体程序很差。 我认为这可以使用 JavaFX 并发来解决,但我从来没有使用过它,所以在我可以更新下面的代码之前,只需忽略while循环之外的东西。

Firstly, I'm using following versions of MariaDB (10.4.12), Java (13.0.2.8), JavaFX (13.0.2), and MariaDB JDBC (2.6.0). 我认为这些都不会产生影响,但以防万一……我使用的是 FXML,这就是为什么你在任何地方都看不到任何 UI 格式的原因。

这是在我的 JavaFX 程序中生成 TreeView 的完整方法。 然后在生成StageScene对象后不久从单独的 class 调用它。

它们的关键部分是while循环,这对我来说是一场斗争。 我最初认为我需要一个嵌套的while while(rs.next())循环,但后来我意识到这会导致行被跳过,因为每个rs.next()调用都与前一个调用相关,而不是与它被使用。

另请注意,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();
                }
            }
        }
    }

将来我可能会找到改进它的方法,但现在我很高兴让它工作。 忽略凌乱的异常处理 - 它正在进行中!

这是我尝试如何在不阻塞 UI 线程的情况下做到这一点:

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;
}

结果豆:

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

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

最后,用你的逻辑构建树:

    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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM