Transactor tx = (Transactor) Thread.currentThread ();
    public void updateNode (IDatabase db, ITransaction txn, Node node) throws Exception {	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 {	    TableDataSet tds = null;	    try {	        tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());	        Record rec = tds.addRecord ();	        rec.setValue (dbm.getIDField (), node.getID ());	        int updated = 0;	        for (Iterator i=dbm.getProp2DB().entrySet().iterator(); i.hasNext(); ) {	            Map.Entry e = (Map.Entry) i.next ();	            String propname = (String) e.getKey ();	            Relation rel = (Relation) e.getValue ();	            // skip properties that don't need to be updated before fetching them	            if (rel != null && (rel.readonly || rel.virtual ||	            		(rel.reftype != Relation.REFERENCE && rel.reftype != Relation.PRIMITIVE)))	                continue;	            Property p = node.getProperty (propname, false);	            if (p != null && rel != null) {	                if (p.dirty) {	                    switch (p.getType ()) {	                        case IProperty.STRING:	                            updated++;	                            rec.setValue (rel.getDbField(), p.getStringValue ());	                            break;	                        case IProperty.BOOLEAN:	                            updated++;	                            rec.setValue (rel.getDbField(), p.getBooleanValue ());	                            break;	                        case IProperty.DATE:	                            updated++;	                            Timestamp t = new Timestamp (p.getDateValue ().getTime ());	                            rec.setValue (rel.getDbField(), t);	                            break;	                        case IProperty.INTEGER:	                            updated++;	                            rec.setValue (rel.getDbField(), p.getIntegerValue ());	                            break;	                        case IProperty.FLOAT:	                            updated++;	                            rec.setValue (rel.getDbField(), p.getFloatValue ());	                            break;	                        case IProperty.NODE:	                            if (!rel.virtual && rel.reftype == Relation.REFERENCE) {	                                // INode n = p.getNodeValue ();	                                // String foreignID = n == null ? null : n.getID ();	                                updated++;	                                rec.setValue (rel.getDbField(), p.getStringValue ());	                            }	                            break;	                    }	                    p.dirty = false;	                    if (!rel.isPrivate())	                        markMappingAsUpdated = true;	                }	            } else if (rel != null && rel.getDbField() != null) {	                updated++;	                rec.setValueNull (rel.getDbField());	            }	        }	        if (updated > 0) {	            // mark the key value as clean so no try is made to update it	            rec.markValueClean (dbm.getIDField ());	            rec.markForUpdate ();	            tds.save ();	        }	    } finally {	        if (tds != null) try {	            tds.close ();	        } catch (Exception ignore) {}	    }	    if (markMappingAsUpdated)	        dbm.notifyDataChange ();	}	// 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 ();	}	// tx.timer.endEvent ("updateNode "+node);    }