Implemented experimental prefetchChildren method on nodes. Try calling

prefetchChildren(start, length) on internal node objects. Works also on nodes
with groupby-collections.

Null columns from the DB are now set to Properties, which required some fixes
when converting a string (or other) property to a node reference.
This commit is contained in:
hns 2002-09-12 17:18:45 +00:00
parent 7c759d694f
commit 2a4d03ac13
4 changed files with 205 additions and 45 deletions

View file

@ -282,12 +282,14 @@ public final class Node implements INode, Serializable {
Value val = rec.getValue (rel.getDbField ());
if (val.isNull ())
continue;
// if (val.isNull ())
// continue;
Property newprop = new Property (rel.propName, this);
switch (val.type ()) {
if (val.isNull ())
newprop.setStringValue (null);
else switch (val.type ()) {
case Types.BIT:
newprop.setBooleanValue (val.asBoolean());
@ -335,7 +337,9 @@ public final class Node implements INode, Serializable {
break;
case Types.NULL:
continue;
newprop.setStringValue (null);
break;
// continue;
default:
newprop.setStringValue (val.asString());
@ -345,15 +349,17 @@ public final class Node implements INode, Serializable {
if(propMap == null)
propMap = new Hashtable ();
propMap.put (rel.propName.toLowerCase(), newprop);
// mark property as clean, since it's fresh from the db
newprop.dirty = false;
// if the property is a pointer to another node, change the property type to NODE
if (rel.reftype == Relation.REFERENCE && rel.usesPrimaryKey ()) {
// FIXME: References to anything other than the primary key are not supported
newprop.nhandle = new NodeHandle (new DbKey (rel.otherType, newprop.getStringValue ()));
newprop.type = IProperty.NODE;
newprop.convertToNodeReference (rel.otherType);
// newprop.nhandle = new NodeHandle (new DbKey (rel.otherType, newprop.getStringValue ()));
// newprop.type = IProperty.NODE;
}
// mark property as clean, since it's fresh from the db
newprop.dirty = false;
}
// again set created and lastmodified. This is because
// lastmodified has been been updated, and we want both values to
@ -1109,7 +1115,7 @@ public final class Node implements INode, Serializable {
if (propMap != null) {
for (Enumeration e2 = propMap.elements (); e2.hasMoreElements (); ) {
Property p = (Property) e2.nextElement ();
if (p != null && p.type == Property.NODE)
if (p != null && p.getType() == Property.NODE)
p.unregisterNode ();
}
}
@ -1215,6 +1221,23 @@ public final class Node implements INode, Serializable {
}
}
public void prefetchChildren (int startIndex, int length) throws Exception {
if (length < 1)
return;
if (startIndex < 0)
return;
loadNodes ();
if (subnodes == null)
return;
int l = Math.min (subnodes.size()-startIndex, length);
if (l < 1)
return;
Key[] keys = new Key[l];
for (int i=startIndex; i<startIndex+l; i++)
keys[i] = ((NodeHandle) subnodes.get (i)).getKey ();
nmgr.nmgr.prefetchNodes (this, dbmap.getSubnodeRelation (), keys);
}
public Enumeration getSubnodes () {
loadNodes ();
class Enum implements Enumeration {
@ -1290,10 +1313,9 @@ public final class Node implements INode, Serializable {
// See if this could be a relationally linked node which still doesn't know
// (i.e, still thinks it's just the key as a string)
DbMapping pmap = dbmap == null ? null : dbmap.getExactPropertyMapping (propname);
if (pmap != null && prop != null && prop.type != IProperty.NODE) {
// this is a relational node stored by id but we still think it's just a string. fix it
prop.nhandle = new NodeHandle (new DbKey (pmap, prop.getStringValue ()));
prop.type = IProperty.NODE;
if (pmap != null && prop != null && prop.getType() != IProperty.NODE) {
// this is a relational node stored by id but we still think it's just a string. Fix it
prop.convertToNodeReference (pmap);
}
// the property does not exist in our propmap - see if we can create it on the fly,
@ -1645,7 +1667,7 @@ public final class Node implements INode, Serializable {
Property prop = propMap == null ? null : (Property) propMap.get (p2);
if (prop != null) {
if (prop.type == IProperty.NODE && n.getHandle ().equals (prop.nhandle)) {
if (prop.getType() == IProperty.NODE && n.getHandle ().equals (prop.getNodeHandle())) {
// nothing to do, just clean up locks and return
if (state == CLEAN) clearWriteLock ();
if (n.state == CLEAN) n.clearWriteLock ();
@ -1705,7 +1727,7 @@ public final class Node implements INode, Serializable {
Property p = (Property) propMap.remove (propname.toLowerCase ());
if (p != null) {
checkWriteLock ();
if (p.type == Property.NODE)
if (p.getType() == Property.NODE)
p.unregisterNode ();
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
lastmodified = System.currentTimeMillis ();

View file

@ -159,7 +159,8 @@ public final class NodeManager {
/**
* Get a node by key.
* Get a node by key. This is called from a node that already holds
* a reference to another node via a NodeHandle/Key.
*/
public Node getNode (Key key) throws Exception {
@ -217,6 +218,8 @@ public final class NodeManager {
/**
* Get a node by relation, using the home node, the relation and a key to apply.
* In contrast to getNode (Key key), this is usually called when we don't yet know
* whether such a node exists.
*/
public Node getNode (Node home, String kstr, Relation rel) throws Exception {
@ -361,7 +364,7 @@ public final class NodeManager {
*/
public void insertNode (IDatabase db, ITransaction txn, Node node) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("insertNode "+node);
DbMapping dbm = node.getDbMapping ();
@ -369,7 +372,7 @@ public final class NodeManager {
if (dbm == null || !dbm.isRelational ()) {
db.saveNode (txn, node.getID (), node);
} else {
app.logEvent ("inserting relational node: "+node.getID ());
// app.logEvent ("inserting relational node: "+node.getID ());
TableDataSet tds = null;
try {
tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
@ -439,7 +442,7 @@ public final class NodeManager {
*/
public void updateNode (IDatabase db, ITransaction txn, Node node) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("updateNode "+node);
DbMapping dbm = node.getDbMapping ();
@ -542,7 +545,7 @@ public final class NodeManager {
*/
public void deleteNode (IDatabase db, ITransaction txn, Node node) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("deleteNode "+node);
DbMapping dbm = node.getDbMapping ();
@ -572,7 +575,7 @@ public final class NodeManager {
*/
public synchronized String generateMaxID (DbMapping map) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("generateID "+map);
QueryDataSet qds = null;
@ -606,7 +609,7 @@ public final class NodeManager {
*/
public String generateID (DbMapping map) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("generateID "+map);
QueryDataSet qds = null;
@ -633,7 +636,7 @@ public final class NodeManager {
*/
public List getNodeIDs (Node home, Relation rel) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("getNodeIDs "+home);
if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) {
@ -710,7 +713,7 @@ public final class NodeManager {
if (rel.groupby != null)
return getNodeIDs (home, rel);
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("getNodes "+home);
if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) {
@ -766,6 +769,109 @@ public final class NodeManager {
}
}
/**
*
*/
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) {
TableDataSet tds = new TableDataSet (dbm.getConnection (),
dbm.getSchema (),
dbm.getKeyDef ());
try {
String idfield = rel.groupby != null ? rel.groupby : dbm.getIDField ();
boolean needsQuotes = dbm.needsQuotes (idfield);
StringBuffer whereBuffer = new StringBuffer (idfield);
whereBuffer.append (" in (");
boolean first = true;
for (int i=0; i<keys.length; i++) {
if (keys[i] != null) {
if (!first)
whereBuffer.append (',');
else
first = false;
if (needsQuotes) {
whereBuffer.append ("'");
whereBuffer.append (escape (keys[i].getID ()));
whereBuffer.append ("'");
} else {
whereBuffer.append (keys[i].getID ());
}
}
}
whereBuffer.append (')');
if (rel.groupby != null) {
whereBuffer.insert (0, rel.renderConstraints (home, home.getNonVirtualParent ()));
}
tds.where (whereBuffer.toString ());
if (logSql)
app.logEvent ("### prefetchNodes: "+tds.getSelectString());
tds.fetchRecords ();
String groupbyProp = null;
HashMap groupbySubnodes = null;
if (rel.groupby != null) {
groupbyProp = dbm.columnNameToProperty (rel.groupby);
groupbySubnodes = new HashMap();
}
for (int i=0; i<tds.size (); i++) {
// create new Nodes.
Record rec = tds.getRecord (i);
Node node = new Node (dbm, rec, safe);
Key primKey = node.getKey ();
// for grouped nodes, collect subnode lists for the intermediary
// group nodes.
if (groupbyProp != null) {
String groupbyName = node.getString (groupbyProp, false);
List sn = (List) groupbySubnodes.get (groupbyName);
if (sn == null) {
sn = new ExternalizableVector ();
groupbySubnodes.put (groupbyName, sn);
}
sn.add (new NodeHandle (primKey));
}
// 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) {
cache.put (primKey, oldnode);
}
}
}
// 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);
cache.put (groupnode.getKey(), groupnode);
groupnode.setSubnodes ((List) groupbySubnodes.get(groupname));
groupnode.lastSubnodeFetch = System.currentTimeMillis ();
}
}
} finally {
if (tds != null) try {
tds.close ();
} catch (Exception ignore) {}
}
}
}
}
/**
* Count the nodes contained in the child collection of the home node
@ -773,7 +879,7 @@ public final class NodeManager {
*/
public int countNodes (Node home, Relation rel) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("countNodes "+home);
if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) {
@ -822,7 +928,7 @@ public final class NodeManager {
*/
public Vector getPropertyNames (Node home, Relation rel) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("getNodeIDs "+home);
if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) {

View file

@ -16,18 +16,18 @@ import helma.objectmodel.*;
public final class Property implements IProperty, Serializable, Cloneable {
protected String propname;
protected Node node;
private String propname;
private Node node;
protected String svalue;
protected boolean bvalue;
protected long lvalue;
protected double dvalue;
private String svalue;
private boolean bvalue;
private long lvalue;
private double dvalue;
// protected String nvalueID;
protected NodeHandle nhandle;
protected Object jvalue;
private NodeHandle nhandle;
private Object jvalue;
protected int type;
private int type;
transient boolean dirty;
@ -221,6 +221,19 @@ public final class Property implements IProperty, Serializable, Cloneable {
dirty = true;
}
public NodeHandle getNodeHandle () {
return nhandle;
}
public void convertToNodeReference (DbMapping dbm) {
String id = getStringValue ();
if (id == null)
nhandle = null;
else
nhandle = new NodeHandle (new DbKey (dbm, id));
type = NODE;
}
public void setJavaObjectValue (Object value) {
if (type == NODE)
unregisterNode ();
@ -281,7 +294,7 @@ public final class Property implements IProperty, Serializable, Cloneable {
case FLOAT:
return Double.toString (dvalue);
case NODE:
return nhandle.getID ();
return nhandle == null ? null : nhandle.getID ();
case JAVAOBJECT:
return jvalue == null ? null : jvalue.toString ();
}

View file

@ -342,6 +342,8 @@ public final class Relation {
vr.filter = filter;
vr.constraints = constraints;
vr.addConstraint (new Constraint (null, null, groupby, true));
vr.aggressiveLoading = aggressiveLoading;
vr.aggressiveCaching = aggressiveCaching;
return vr;
}
@ -393,7 +395,9 @@ public final class Relation {
q.append (filter);
}
if (groupby != null) {
q.append (" GROUP BY "+groupby);
q.append (prefix);
q.append (groupby);
q.append (" IS NOT NULL GROUP BY "+groupby);
if (useOrder && groupbyorder != null)
q.append (" ORDER BY "+groupbyorder);
} else if (useOrder && order != null)
@ -401,6 +405,21 @@ public final class Relation {
return q.toString ();
}
public String renderConstraints (INode home, INode nonvirtual) throws SQLException {
StringBuffer q = new StringBuffer ();
String suffix = " AND ";
for (int i=0; i<constraints.length; i++) {
constraints[i].addToQuery (q, home, nonvirtual);
q.append (suffix);
}
if (filter != null) {
q.append (filter);
q.append (suffix);
}
return q.toString ();
}
/**
* Get the order section to use for this relation
*/