【问题标题】:Populate a DefaultMutableTreeNode from database从数据库填充 DefaultMutableTreeNode
【发布时间】:2012-06-25 21:01:36
【问题描述】:

我是 Java 新手,我想创建一个包含 Map 对象的 DefaultMutableTreeNode 类。

我编写了一个 TreeNodeMap 类,它继承自一个更通用的 TreeNode 类,而后者又继承自 DefaultMutableTreeNode。

TreeNodeMap 类包含方法populate,它接受相当长的参数列表。我计划通过将其中一些参数转换为单个对象并重载方法来提高方法的可读性,这样我就不必在第一次调用中传递一组空值(递归)。

注意:如果您不关心我对方法的详细解释,请直接从此处跳至代码

最初我创建了一个空节点,它只包含子节点但不包含数据,这是 ROOT。这个想法是有一个方法,允许调用者传递任何带有“field_id”、“field_label”、“parent_id”列的查询。在初始调用中,查询通过 Where 子句传递,其中“parent_id 为 null”,因此得到所有没有父节点的节点。所有这些节点都添加到 ROOT 节点。在遍历结果集时,每个节点的 populate 方法现在被称为传递一个“Where 子句”,其中 parent_id = [当前节点 id] 因此获取该节点的所有子节点。这将递归地发生,直到所有节点都具有层次结构。

代码 (请记住,我是 JAVA 新手,因此感谢任何反馈)

public void populate( boolean isRoot, String parentFieldId, String parentFieldLabel, String childFieldId, String childFieldLabel, 
            int parentId, String tableName, String whereClause, String orderByClause, String additionalColumns, List<Map<String, String>> queryParams) throws SQLException, Exception{
        ResultSet rs = null;
        Connection con = null;
        PreparedStatement ps = null;
        try{

            DBConnection dbConnection = new DBConnection("localhost", 3306, "root", "password", "test", DBDrivers.DBTYPE_MYSQL);
            con = dbConnection.getConnection();

            String treeNodeSql = "Select " + parentFieldId + ", " + parentFieldLabel + 
                                                ", " + childFieldId + ", " + childFieldLabel;
            if(additionalColumns != null && additionalColumns.trim().length() > 0)
                treeNodeSql +=  " ," + additionalColumns;

            treeNodeSql += " From " + tableName + " WHERE 1=1";

            if(whereClause != null && whereClause.trim().length() > 0 ){
                treeNodeSql += " AND " + whereClause;
            }

            if(isRoot){
                treeNodeSql += " AND " + parentFieldId + " is null";
            }else{
                if(parentFieldId == null || parentFieldId.trim().length() == 0)
                    throw new Exception(" The populate() method requires a parentId when isRoot is false.");
                treeNodeSql += " AND " + parentFieldId + " = ?";
            }
                //order clause append
            if(orderByClause != null && orderByClause.trim().length() > 0)
                treeNodeSql += " " + orderByClause;

                //prepare statement
             ps = con.prepareStatement(treeNodeSql); 
            int ixParam = 0;

                for(Map qParam : queryParams){
                    if(qParam.get("datatype") == "int"){
                        ps.setInt(++ixParam, Integer.parseInt((String) qParam.get("value")));
                    }else if(qParam.get("datatype") == "string"){
                        ps.setString(++ixParam, (String) qParam.get("value"));
                    }
                }

            out.println(treeNodeSql);
            if(parentId > 0){
                ps.setInt(queryParams.size()+1, parentId);
            }
            rs = ps.executeQuery();

            while(rs.next()){
                HashMap<String, Object> childNodeData = new HashMap<String, Object>(4);
                childNodeData.put("parentFieldId", parentFieldId);
                childNodeData.put("parentFieldIdValue", Integer.toString(rs.getInt(parentFieldId)));
                childNodeData.put("parentFieldLabel", parentFieldLabel);
                childNodeData.put("parentFieldLabelValue", rs.getString(parentFieldLabel));
                childNodeData.put("childFieldId", childFieldId);
                childNodeData.put("childFieldIdValue", Integer.toString(rs.getInt(childFieldId)));
                childNodeData.put("childFieldLabel", childFieldLabel);
                childNodeData.put("childFieldLabelValue", rs.getString(childFieldLabel));

                out.println("parentId: " + rs.getInt(parentFieldId)
                                + ", parentLabel: " + rs.getString(parentFieldLabel)
                                + ", childId: " + rs.getInt(childFieldId)
                                + ", childLabel: " + rs.getString(childFieldLabel));
                TreeNodeMap childNode = new TreeNodeMap(childNodeData);
                this.add(childNode);
                childNode.populate(false, parentFieldId, parentFieldLabel, childFieldId, childFieldLabel, rs.getInt(childFieldId), tableName, whereClause, orderByClause, additionalColumns, queryParams);
            }
        }catch(SQLException e){
            throw e;

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            out.println(e.getCause());
            out.println(e.getMessage());
            e.printStackTrace();
            throw e;
        }finally {
            try { rs.close(); } catch (Exception e) {  }
            try { ps.close(); } catch (Exception e) {  }
            try { con.close(); } catch (Exception e) {  }
        }
    }   

对该方法的初始调用如下所示:

treeNode.populate(true, "supervisor_id", "supervisor", "employee_id", "employee", 0, "vw_employee", null, null, null, queryParams);

我使用这种方法看到的问题是数据库将被查询 X 次,并且对于大数据集,我认为这会导致一些问题。我还为每个查询打开一个连接!所以我考虑过在创建连接后将其传递给方法(在递归调用中),但是我不确定如何适当地关闭它。我可以编写一个条件来检查节点是否为 ROOT,然后关闭连接,但如果代码在两者之间失败会发生什么。

最后我的问题是: 1-解决这个问题的最佳方法是什么? 2-我是否应该传递连接,以便在树的填充期间只有一个连接保持打开状态?如果是,那么我该如何正确关闭它。 3- 我应该将结果集缓存到 ArrayList 中并改用它吗?

【问题讨论】:

    标签: java swing jdbc jtree treenode


    【解决方案1】:

    您的性能问题可能是有根据的。相反,

    1. 实现TreeModel,如FileTreeModel所示。这样,只需要查询可见节点。有一个相关的例子here

    2. 传递javax.sql.DataSource,而不是Connection

    3. implements TreeModel 的类可以封装任何缓存的数据,但也提供了一些更新陈旧条目的方法。显示缓存值后,考虑使用SwingWorker刷新模型,如图here。您可能想查看Map&lt;PrimaryKey, Record&gt;,而不是List&lt;Record&gt;

    【讨论】:

    • 非常感谢@trashgod 的建议。这里有几件事要消化,我已经在看你的建议了。我不知道 javax.sql.Datasource,似乎在做与我的 DBConnection 类相同的事情,并且没有必要重新发明轮子,所以我很可能会使用那个。一旦我了解您的所有建议,我将继续阅读并回复您。再次感谢!
    猜你喜欢
    • 2010-09-26
    • 2011-07-01
    • 2021-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 2012-07-22
    • 1970-01-01
    相关资源
    最近更新 更多