if (logSql) { long logTimeStop = System.currentTimeMillis(); logSqlStatement("SQL UPDATE", dbm.getTableName(), logTimeStart, logTimeStop, b.toString()); }
    public boolean updateNode(IDatabase db, ITransaction txn, Node node)                    throws IOException, SQLException, ClassNotFoundException {        // Transactor tx = (Transactor) Thread.currentThread ();        // tx.timer.beginEvent ("updateNode "+node);        DbMapping dbm = node.getDbMapping();        boolean markMappingAsUpdated = false;        if ((dbm == null) || !dbm.isRelational()) {            db.saveNode(txn, node.getID(), node);        } else {            Hashtable propMap = node.getPropMap();            Property[] props;            if (propMap == null) {                props = new Property[0];            } else {                props = new Property[propMap.size()];                propMap.values().toArray(props);            }            // make sure table meta info is loaded by dbmapping            dbm.getColumns();            StringBuffer b = dbm.getUpdate();            // comma flag set after the first dirty column, also tells as            // if there are dirty columns at all            boolean comma = false;            for (int i = 0; i < props.length; i++) {                // skip clean properties                if ((props[i] == null) || !props[i].dirty) {                    // null out clean property so we don't consider it later                    props[i] = null;                    continue;                }                Relation rel = dbm.propertyToRelation(props[i].getName());                // skip readonly, virtual and collection relations                if ((rel == null) || rel.readonly || rel.virtual ||                        ((rel.reftype != Relation.REFERENCE) &&                        (rel.reftype != Relation.PRIMITIVE))) {                    // null out property so we don't consider it later                    props[i] = null;                    continue;                }                if (comma) {                    b.append(", ");                } else {                    comma = true;                }                b.append(rel.getDbField());                b.append(" = ?");            }            // if no columns were updated, return false            if (!comma) {                return false;            }            b.append(" WHERE ");            b.append(dbm.getIDField());            b.append(" = ");            if (dbm.needsQuotes(dbm.getIDField())) {                b.append("'");                b.append(escape(node.getID()));                b.append("'");            } else {                b.append(node.getID());            }            Connection con = dbm.getConnection();            PreparedStatement stmt = con.prepareStatement(b.toString());            if (logSql) {                app.logEvent("### updateNode: " + b.toString());            }            int stmtNumber = 0;            try {                for (int i = 0; i < props.length; i++) {                    Property p = props[i];                    if (p == null) {                        continue;                    }                    Relation rel = dbm.propertyToRelation(p.getName());                    stmtNumber++;                    this.setStatementValues (stmt, stmtNumber, p, rel.getColumnType());                    p.dirty = false;                    if (!rel.isPrivate()) {                        markMappingAsUpdated = true;                    }                }                stmt.executeUpdate();            } finally {                if (stmt != null) {                    try {                        stmt.close();                    } catch (Exception ignore) {                    }                }            }        }        // update may cause changes in the node's parent subnode array        if (node.isAnonymous()) {            Node parent = node.getCachedParent();            if (parent != null) {                parent.lastSubnodeChange = System.currentTimeMillis();            }        }        return markMappingAsUpdated;    }