StringBuffer b = dbm.getSelect(rel);
StringBuffer b = dbm.getSelect(null);
    public void prefetchNodes(Node home, Relation rel, Key[] keys)                       throws Exception {        DbMapping dbm = rel.otherType;        if ((dbm == null) || !dbm.isRelational()) {            // this does nothing for objects in the embedded database            return;        } else {            int missing = cache.containsKeys(keys);            if (missing > 0) {                Connection con = dbm.getConnection();                // set connection to read-only mode                if (!con.isReadOnly()) con.setReadOnly(true);                Statement stmt = con.createStatement();                DbColumn[] columns = dbm.getColumns();                Relation[] joins = dbm.getJoins();                String query = null;                long logTimeStart = logSql ? System.currentTimeMillis() : 0;                try {                    StringBuffer b = dbm.getSelect(rel);                    String idfield = (rel.groupby != null) ? rel.groupby : dbm.getIDField();                    boolean needsQuotes = dbm.needsQuotes(idfield);                    b.append(" WHERE ");                    b.append(dbm.getTableName());                    b.append(".");                    b.append(idfield);                    b.append(" IN (");                    boolean first = true;                    for (int i = 0; i < keys.length; i++) {                        if (keys[i] != null) {                            if (!first) {                                b.append(',');                            } else {                                first = false;                            }                            if (needsQuotes) {                                b.append("'");                                b.append(escape(keys[i].getID()));                                b.append("'");                            } else {                                b.append(keys[i].getID());                            }                        }                    }                    b.append(") ");                    dbm.addJoinConstraints(b, " AND ");                    if (rel.groupby != null) {                        rel.renderConstraints(b, home, home.getNonVirtualParent(), " AND ");                        if (rel.order != null) {                            b.append(" ORDER BY ");                            b.append(rel.order);                        }                    }                    query = b.toString();                    ResultSet rs = stmt.executeQuery(query);                    String groupbyProp = null;                    HashMap groupbySubnodes = null;                    if (rel.groupby != null) {                        groupbyProp = dbm.columnNameToProperty(rel.groupby);                        groupbySubnodes = new HashMap();                    }                    String accessProp = null;                    if ((rel.accessName != null) && !rel.usesPrimaryKey()) {                        accessProp = dbm.columnNameToProperty(rel.accessName);                    }                    while (rs.next()) {                        // create new Nodes.                        Node node = createNode(dbm, rs, columns, 0);                        if (node == null) {                            continue;                        }                        Key primKey = node.getKey();                        // for grouped nodes, collect subnode lists for the intermediary                        // group nodes.                        String groupName = null;                        if (groupbyProp != null) {                            groupName = node.getString(groupbyProp);                            SubnodeList sn = (SubnodeList) groupbySubnodes.get(groupName);                            if (sn == null) {                                sn = new SubnodeList(safe, rel);                                groupbySubnodes.put(groupName, sn);                            }                            sn.addSorted(new NodeHandle(primKey));                        }                        // if relation doesn't use primary key as accessName, get accessName value                        String accessName = null;                        if (accessProp != null) {                            accessName = node.getString(accessProp);                        }                        // register new nodes with the cache. If an up-to-date copy                        // existed in the cache, use that.                        synchronized (cache) {                            Node oldnode = (Node) cache.put(primKey, node);                            if ((oldnode != null) &&                                    (oldnode.getState() != INode.INVALID)) {                                // found an ok version in the cache, use it.                                cache.put(primKey, oldnode);                            } else if (accessName != null) {                                // put the node into cache with its secondary key                                if (groupName != null) {                                    cache.put(new SyntheticKey(new SyntheticKey(home.getKey(),                                                                                groupName),                                                               accessName), node);                                } else {                                    cache.put(new SyntheticKey(home.getKey(), accessName),                                              node);                                }                            }                        }                        fetchJoinedNodes(rs, joins, columns.length);                    }                    // If these are grouped nodes, build the intermediary group nodes                    // with the subnod lists we created                    if (groupbyProp != null) {                        for (Iterator i = groupbySubnodes.keySet().iterator();                                 i.hasNext();) {                            String groupname = (String) i.next();                            if (groupname == null) {                                continue;                            }                            Node groupnode = home.getGroupbySubnode(groupname, true);                            groupnode.setSubnodes((SubnodeList) groupbySubnodes.get(groupname));                            groupnode.lastSubnodeFetch = System.currentTimeMillis();                        }                    }                } catch (Exception x) {                    System.err.println ("Error in prefetchNodes(): "+x);                } finally {                    if (logSql) {                        long logTimeStop = System.currentTimeMillis();                        logSqlStatement("SQL SELECT_PREFETCH", dbm.getTableName(),                                        logTimeStart, logTimeStop, query);                    }                    if (stmt != null) {                        try {                            stmt.close();                        } catch (Exception ignore) {                        }                    }                }            }        }    }