* Implement mechanism to register parent nodes with changed child nodes with the transactor.
When finishing the transaction, the transactor will call setLastSubnodeChange() on the parent nodes. This is necessary because the lastSubnodeChange flag should only be set after child node changes have been committed to the database. (Fixes bug 285 http://helma.org/bugs/show_bug.cgi?id=285 ) * Changed the way select statements are built: Use tablename.* rather than * to prevent columns from additionalTables are fetched unnecessarily. * Code cleanup everywhere, mostly in Transactor.java, Relation.java and NodeManager.java
This commit is contained in:
parent
8774a52e44
commit
507949310c
3 changed files with 188 additions and 175 deletions
|
@ -246,8 +246,11 @@ public final class Node implements INode, Serializable {
|
||||||
this.subnodes = subnodes;
|
this.subnodes = subnodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void checkWriteLock() {
|
/**
|
||||||
// System.err.println ("registering writelock for "+this.getName ()+" ("+lock+") to "+Thread.currentThread ());
|
* Get the write lock on this node, throwing a ConcurrencyException if the
|
||||||
|
* lock is already held by another thread.
|
||||||
|
*/
|
||||||
|
synchronized void checkWriteLock() {
|
||||||
if (state == TRANSIENT) {
|
if (state == TRANSIENT) {
|
||||||
return; // no need to lock transient node
|
return; // no need to lock transient node
|
||||||
}
|
}
|
||||||
|
@ -275,11 +278,17 @@ public final class Node implements INode, Serializable {
|
||||||
lock = current;
|
lock = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized void clearWriteLock() {
|
/**
|
||||||
|
* Clear the write lock on this node.
|
||||||
|
*/
|
||||||
|
synchronized void clearWriteLock() {
|
||||||
lock = null;
|
lock = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void markAs(int s) {
|
/**
|
||||||
|
* Set this node's state, registering it with the transactor if necessary.
|
||||||
|
*/
|
||||||
|
void markAs(int s) {
|
||||||
if ((state == INVALID) || (state == VIRTUAL) || (state == TRANSIENT)) {
|
if ((state == INVALID) || (state == VIRTUAL) || (state == TRANSIENT)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -304,18 +313,37 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Register this node as parent node with the transactor so that
|
||||||
|
* setLastSubnodeChange is called when the transaction completes.
|
||||||
|
*/
|
||||||
|
void registerSubnodeChange() {
|
||||||
|
if (Thread.currentThread() instanceof Transactor) {
|
||||||
|
Transactor tx = (Transactor) Thread.currentThread();
|
||||||
|
tx.visitParentNode(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the transactor on registered parent nodes to mark the
|
||||||
|
* child index as changed
|
||||||
|
*/
|
||||||
|
void setLastSubnodeChange(long t) {
|
||||||
|
lastSubnodeChange = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets this node's stateas defined in the INode interface
|
||||||
*
|
*
|
||||||
*
|
* @return this node's state
|
||||||
* @return ...
|
|
||||||
*/
|
*/
|
||||||
public int getState() {
|
public int getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Sets this node's state as defined in the INode interface
|
||||||
*
|
*
|
||||||
*
|
* @param s this node's new state
|
||||||
* @param s ...
|
|
||||||
*/
|
*/
|
||||||
public void setState(int s) {
|
public void setState(int s) {
|
||||||
this.state = s;
|
this.state = s;
|
||||||
|
@ -793,8 +821,6 @@ public final class Node implements INode, Serializable {
|
||||||
node.makePersistable();
|
node.makePersistable();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (n.indexOf('/') > -1)
|
|
||||||
// throw new RuntimeException ("\"/\" found in Node name.");
|
|
||||||
// only mark this node as modified if subnodes are not in relational db
|
// only mark this node as modified if subnodes are not in relational db
|
||||||
// pointing to this node.
|
// pointing to this node.
|
||||||
if (!ignoreSubnodeChange() && ((state == CLEAN) || (state == DELETED))) {
|
if (!ignoreSubnodeChange() && ((state == CLEAN) || (state == DELETED))) {
|
||||||
|
@ -830,7 +856,6 @@ public final class Node implements INode, Serializable {
|
||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
System.err.println("Error adding groupby: " + x);
|
System.err.println("Error adding groupby: " + x);
|
||||||
|
|
||||||
// x.printStackTrace ();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,7 +876,6 @@ public final class Node implements INode, Serializable {
|
||||||
if (subnodes == null) {
|
if (subnodes == null) {
|
||||||
subnodes = new ExternalizableVector();
|
subnodes = new ExternalizableVector();
|
||||||
}
|
}
|
||||||
|
|
||||||
subnodes.add(where, nhandle);
|
subnodes.add(where, nhandle);
|
||||||
|
|
||||||
// check if properties are subnodes (_properties.aresubnodes=true)
|
// check if properties are subnodes (_properties.aresubnodes=true)
|
||||||
|
@ -861,7 +885,8 @@ public final class Node implements INode, Serializable {
|
||||||
if ((prel != null) && (prel.accessName != null)) {
|
if ((prel != null) && (prel.accessName != null)) {
|
||||||
Relation localrel = node.dbmap.columnNameToRelation(prel.accessName);
|
Relation localrel = node.dbmap.columnNameToRelation(prel.accessName);
|
||||||
|
|
||||||
// if no relation from db column to prop name is found, assume that both are equal
|
// if no relation from db column to prop name is found,
|
||||||
|
// assume that both are equal
|
||||||
String propname = (localrel == null) ? prel.accessName
|
String propname = (localrel == null) ? prel.accessName
|
||||||
: localrel.propName;
|
: localrel.propName;
|
||||||
String prop = node.getString(propname);
|
String prop = node.getString(propname);
|
||||||
|
@ -880,29 +905,29 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!"root".equalsIgnoreCase(node.getPrototype())) {
|
if (!"root".equalsIgnoreCase(node.getPrototype())) {
|
||||||
// avoid calling getParent() because it would return bogus results for the not-anymore transient node
|
// avoid calling getParent() because it would return bogus results
|
||||||
|
// for the not-anymore transient node
|
||||||
Node nparent = (node.parentHandle == null) ? null
|
Node nparent = (node.parentHandle == null) ? null
|
||||||
: node.parentHandle.getNode(nmgr);
|
: node.parentHandle.getNode(nmgr);
|
||||||
|
|
||||||
// if the node doesn't have a parent yet, or it has one but it's transient while we are
|
// if the node doesn't have a parent yet, or it has one but it's
|
||||||
// persistent, make this the nodes new parent.
|
// transient while we are persistent, make this the nodes new parent.
|
||||||
if ((nparent == null) ||
|
if ((nparent == null) ||
|
||||||
((state != TRANSIENT) && (nparent.getState() == TRANSIENT))) {
|
((state != TRANSIENT) && (nparent.getState() == TRANSIENT))) {
|
||||||
node.setParent(this);
|
node.setParent(this);
|
||||||
node.anonymous = true;
|
node.anonymous = true;
|
||||||
} else if ((nparent != null) && ((nparent != this) || !node.anonymous)) {
|
} else if ((nparent != null) && ((nparent != this) || !node.anonymous)) {
|
||||||
// this makes the additional job of addLink, registering that we have a link to a node in our
|
// this makes the additional job of addLink, registering that we have
|
||||||
// subnodes that actually sits somewhere else. This means that addLink and addNode
|
// a link to a node in our subnodes that actually sits somewhere else.
|
||||||
// are actually the same now.
|
// This means that addLink and addNode are actually the same now.
|
||||||
node.registerLinkFrom(this);
|
node.registerLinkFrom(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
lastSubnodeChange = lastmodified;
|
registerSubnodeChange();
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node));
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,15 +1262,7 @@ public final class Node implements INode, Serializable {
|
||||||
subnodes.remove(node.getHandle());
|
subnodes.remove(node.getHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSubnodeChange = System.currentTimeMillis();
|
registerSubnodeChange();
|
||||||
|
|
||||||
// check if the subnode is in relational db and has a link back to this
|
|
||||||
// which needs to be unset
|
|
||||||
/*
|
|
||||||
if (dbmap != null) {
|
|
||||||
Relation srel = dbmap.getSubnodeRelation();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// check if subnodes are also accessed as properties. If so, also unset the property
|
// check if subnodes are also accessed as properties. If so, also unset the property
|
||||||
if ((dbmap != null) && (node.dbmap != null)) {
|
if ((dbmap != null) && (node.dbmap != null)) {
|
||||||
|
@ -1269,8 +1286,6 @@ public final class Node implements INode, Serializable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (node, NodeEvent.NODE_REMOVED));
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_REMOVED, node));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
// nmgr.logEvent ("released node "+node +" from "+this+" oldobj = "+what);
|
// nmgr.logEvent ("released node "+node +" from "+this+" oldobj = "+what);
|
||||||
|
@ -1354,7 +1369,8 @@ public final class Node implements INode, Serializable {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the node contains relational groupby subnodes, the subnodes vector contains the names instead of ids.
|
// if the node contains relational groupby subnodes, the subnodes vector
|
||||||
|
// contains the names instead of ids.
|
||||||
if (!(n instanceof Node)) {
|
if (!(n instanceof Node)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1399,7 +1415,6 @@ public final class Node implements INode, Serializable {
|
||||||
subnodeCount = nmgr.countNodes(this, subRel);
|
subnodeCount = nmgr.countNodes(this, subRel);
|
||||||
lastSubnodeCount = System.currentTimeMillis();
|
lastSubnodeCount = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
return subnodeCount;
|
return subnodeCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1599,7 +1614,8 @@ public final class Node implements INode, Serializable {
|
||||||
DbMapping pmap = (dbmap == null) ? null : dbmap.getExactPropertyMapping(propname);
|
DbMapping pmap = (dbmap == null) ? null : dbmap.getExactPropertyMapping(propname);
|
||||||
|
|
||||||
if ((pmap != null) && (prop != null) && (prop.getType() != 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
|
// this is a relational node stored by id but we still think it's just a string.
|
||||||
|
// Fix it.
|
||||||
prop.convertToNodeReference(pmap);
|
prop.convertToNodeReference(pmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1619,11 +1635,13 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// so if we have a property relation and it does in fact link to another object...
|
// so if we have a property relation and it does in fact link to another object...
|
||||||
if ((propRel != null) && (propRel.isCollection() || propRel.isComplexReference())) {
|
if ((propRel != null) && (propRel.isCollection() ||
|
||||||
// in some cases we just want to create and set a generic node without consulting
|
propRel.isComplexReference())) {
|
||||||
// the NodeManager if it exists: When we get a collection (aka virtual node)
|
// in some cases we just want to create and set a generic node without
|
||||||
// from a transient node for the first time, or when we get a collection whose
|
// consulting the NodeManager if it exists: When we get a collection
|
||||||
// content objects are stored in the embedded XML data storage.
|
// (aka virtual node) from a transient node for the first time, or when
|
||||||
|
// we get a collection whose content objects are stored in the embedded
|
||||||
|
// XML data storage.
|
||||||
if ((state == TRANSIENT) && propRel.virtual) {
|
if ((state == TRANSIENT) && propRel.virtual) {
|
||||||
Node pn = new Node(propname, propRel.getPrototype(), nmgr);
|
Node pn = new Node(propname, propRel.getPrototype(), nmgr);
|
||||||
|
|
||||||
|
@ -1827,7 +1845,6 @@ public final class Node implements INode, Serializable {
|
||||||
propMap.put(p2, prop);
|
propMap.put(p2, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -1922,8 +1939,9 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
if (((oldStorage == null) && (newStorage == null)) ||
|
if (((oldStorage == null) && (newStorage == null)) ||
|
||||||
((oldStorage != null) && oldStorage.equals(newStorage))) {
|
((oldStorage != null) && oldStorage.equals(newStorage))) {
|
||||||
dbmap.notifyDataChange();
|
long now = System.currentTimeMillis();
|
||||||
newmap.notifyDataChange();
|
dbmap.setLastDataChange(now);
|
||||||
|
newmap.setLastDataChange(now);
|
||||||
this.dbmap = newmap;
|
this.dbmap = newmap;
|
||||||
this.prototype = value;
|
this.prototype = value;
|
||||||
}
|
}
|
||||||
|
@ -1931,7 +1949,6 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -1967,7 +1984,6 @@ public final class Node implements INode, Serializable {
|
||||||
propMap.put(p2, prop);
|
propMap.put(p2, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -2003,7 +2019,6 @@ public final class Node implements INode, Serializable {
|
||||||
propMap.put(p2, prop);
|
propMap.put(p2, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -2039,7 +2054,6 @@ public final class Node implements INode, Serializable {
|
||||||
propMap.put(p2, prop);
|
propMap.put(p2, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -2075,7 +2089,6 @@ public final class Node implements INode, Serializable {
|
||||||
propMap.put(p2, prop);
|
propMap.put(p2, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -2111,7 +2124,6 @@ public final class Node implements INode, Serializable {
|
||||||
propMap.put(p2, prop);
|
propMap.put(p2, prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -2288,7 +2300,6 @@ public final class Node implements INode, Serializable {
|
||||||
p.setStringValue(null);
|
p.setStringValue(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
|
@ -2488,4 +2499,5 @@ public final class Node implements INode, Serializable {
|
||||||
System.err.println("properties: " + propMap);
|
System.err.println("properties: " + propMap);
|
||||||
System.err.println("links: " + links);
|
System.err.println("links: " + links);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ public final class NodeManager {
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
System.err.println(">> " + x);
|
System.err.println(x);
|
||||||
x.printStackTrace();
|
x.printStackTrace();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -214,8 +214,9 @@ public final class NodeManager {
|
||||||
node = (Node) cache.get(key);
|
node = (Node) cache.get(key);
|
||||||
|
|
||||||
if ((node == null) || (node.getState() == Node.INVALID)) {
|
if ((node == null) || (node.getState() == Node.INVALID)) {
|
||||||
// The requested node isn't in the shared cache. Synchronize with key to make sure only one
|
// The requested node isn't in the shared cache.
|
||||||
// version is fetched from the database.
|
// Synchronize with key to make sure only one version is
|
||||||
|
// fetched from the database.
|
||||||
if (key instanceof SyntheticKey) {
|
if (key instanceof SyntheticKey) {
|
||||||
Node parent = getNode(key.getParentKey());
|
Node parent = getNode(key.getParentKey());
|
||||||
Relation rel = parent.dbmap.getPropertyRelation(key.getID());
|
Relation rel = parent.dbmap.getPropertyRelation(key.getID());
|
||||||
|
@ -249,7 +250,6 @@ public final class NodeManager {
|
||||||
tx.visitCleanNode(key, node);
|
tx.visitCleanNode(key, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tx.timer.endEvent ("getNode "+kstr);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,10 +289,11 @@ public final class NodeManager {
|
||||||
Node node = tx.getVisitedNode(key);
|
Node node = tx.getVisitedNode(key);
|
||||||
|
|
||||||
if ((node != null) && (node.getState() != Node.INVALID)) {
|
if ((node != null) && (node.getState() != Node.INVALID)) {
|
||||||
// we used to refresh the node in the main cache here to avoid the primary key entry being
|
// we used to refresh the node in the main cache here to avoid the primary key
|
||||||
// flushed from cache before the secondary one (risking duplicate nodes in cache) but
|
// entry being flushed from cache before the secondary one
|
||||||
// we don't need to since we fetched the node from the threadlocal transactor cache and
|
// (risking duplicate nodes in cache) but we don't need to since we fetched
|
||||||
// didn't refresh it in the main cache.
|
// the node from the threadlocal transactor cache and didn't refresh it in the
|
||||||
|
// main cache.
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,8 +327,9 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((node == null) || (node.getState() == Node.INVALID)) {
|
if ((node == null) || (node.getState() == Node.INVALID)) {
|
||||||
// The requested node isn't in the shared cache. Synchronize with key to make sure only one
|
// The requested node isn't in the shared cache.
|
||||||
// version is fetched from the database.
|
// Synchronize with key to make sure only one version is fetched
|
||||||
|
// from the database.
|
||||||
node = getNodeByRelation(tx.txn, home, kstr, rel);
|
node = getNodeByRelation(tx.txn, home, kstr, rel);
|
||||||
|
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
|
@ -459,10 +461,7 @@ public final class NodeManager {
|
||||||
db.saveNode(txn, node.getID(), node);
|
db.saveNode(txn, node.getID(), node);
|
||||||
} else {
|
} else {
|
||||||
insertRelationalNode(node, dbm, dbm.getConnection());
|
insertRelationalNode(node, dbm, dbm.getConnection());
|
||||||
dbm.notifyDataChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tx.timer.endEvent ("insertNode "+node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -645,8 +644,11 @@ public final class NodeManager {
|
||||||
/**
|
/**
|
||||||
* Updates a modified node in the embedded db or an external relational database, depending
|
* Updates a modified node in the embedded db or an external relational database, depending
|
||||||
* on its database mapping.
|
* on its database mapping.
|
||||||
|
*
|
||||||
|
* @return true if the DbMapping of the updated Node is to be marked as updated via
|
||||||
|
* DbMapping.setLastDataChange
|
||||||
*/
|
*/
|
||||||
public void updateNode(IDatabase db, ITransaction txn, Node node)
|
public boolean updateNode(IDatabase db, ITransaction txn, Node node)
|
||||||
throws IOException, SQLException, ClassNotFoundException {
|
throws IOException, SQLException, ClassNotFoundException {
|
||||||
// Transactor tx = (Transactor) Thread.currentThread ();
|
// Transactor tx = (Transactor) Thread.currentThread ();
|
||||||
// tx.timer.beginEvent ("updateNode "+node);
|
// tx.timer.beginEvent ("updateNode "+node);
|
||||||
|
@ -666,6 +668,8 @@ public final class NodeManager {
|
||||||
|
|
||||||
StringBuffer b = dbm.getUpdate();
|
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;
|
boolean comma = false;
|
||||||
|
|
||||||
for (int i = 0; i < props.length; i++) {
|
for (int i = 0; i < props.length; i++) {
|
||||||
|
@ -699,9 +703,9 @@ public final class NodeManager {
|
||||||
b.append(" = ?");
|
b.append(" = ?");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no columns were updated, return
|
// if no columns were updated, return false
|
||||||
if (!comma) {
|
if (!comma) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
b.append(" WHERE ");
|
b.append(" WHERE ");
|
||||||
|
@ -822,10 +826,6 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markMappingAsUpdated) {
|
|
||||||
dbm.notifyDataChange();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update may cause changes in the node's parent subnode array
|
// update may cause changes in the node's parent subnode array
|
||||||
|
@ -837,16 +837,15 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tx.timer.endEvent ("updateNode "+node);
|
return markMappingAsUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the actual deletion of a node from either the embedded or an external SQL database.
|
* Performs the actual deletion of a node from either the embedded or an external
|
||||||
|
* SQL database.
|
||||||
*/
|
*/
|
||||||
public void deleteNode(IDatabase db, ITransaction txn, Node node)
|
public void deleteNode(IDatabase db, ITransaction txn, Node node)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// Transactor tx = (Transactor) Thread.currentThread ();
|
|
||||||
// tx.timer.beginEvent ("deleteNode "+node);
|
|
||||||
DbMapping dbm = node.getDbMapping();
|
DbMapping dbm = node.getDbMapping();
|
||||||
|
|
||||||
if ((dbm == null) || !dbm.isRelational()) {
|
if ((dbm == null) || !dbm.isRelational()) {
|
||||||
|
@ -877,14 +876,10 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbm.notifyDataChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// node may still be cached via non-primary keys. mark as invalid
|
// node may still be cached via non-primary keys. mark as invalid
|
||||||
node.setState(Node.INVALID);
|
node.setState(Node.INVALID);
|
||||||
|
|
||||||
// tx.timer.endEvent ("deleteNode "+node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -994,23 +989,20 @@ public final class NodeManager {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String q = null;
|
String q = null;
|
||||||
|
StringBuffer b = new StringBuffer("SELECT ").append(table).append('.')
|
||||||
|
.append(idfield).append(" FROM ")
|
||||||
|
.append(table);
|
||||||
|
|
||||||
if (home.getSubnodeRelation() != null) {
|
if (home.getSubnodeRelation() != null) {
|
||||||
// subnode relation was explicitly set
|
// subnode relation was explicitly set
|
||||||
q = new StringBuffer("SELECT ").append(table).append('.')
|
q = b.append(" ").append(home.getSubnodeRelation()).toString();
|
||||||
.append(idfield).append(" FROM ")
|
|
||||||
.append(table).append(" ")
|
|
||||||
.append(home.getSubnodeRelation())
|
|
||||||
.toString();
|
|
||||||
} else {
|
} else {
|
||||||
// let relation object build the query
|
// let relation object build the query
|
||||||
q = new StringBuffer("SELECT ").append(idfield).append(" FROM ")
|
q = b.append(rel.buildQuery(home,
|
||||||
.append(table)
|
home.getNonVirtualParent(),
|
||||||
.append(rel.buildQuery(home,
|
null,
|
||||||
home.getNonVirtualParent(),
|
" WHERE ",
|
||||||
null,
|
true)).toString();
|
||||||
" WHERE ", true))
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logSql) {
|
if (logSql) {
|
||||||
|
@ -1099,8 +1091,11 @@ public final class NodeManager {
|
||||||
q.append(home.getSubnodeRelation());
|
q.append(home.getSubnodeRelation());
|
||||||
} else {
|
} else {
|
||||||
// let relation object build the query
|
// let relation object build the query
|
||||||
q.append(rel.buildQuery(home, home.getNonVirtualParent(), null,
|
q.append(rel.buildQuery(home,
|
||||||
" WHERE ", true));
|
home.getNonVirtualParent(),
|
||||||
|
null,
|
||||||
|
" WHERE ",
|
||||||
|
true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logSql) {
|
if (logSql) {
|
||||||
|
@ -1341,21 +1336,18 @@ public final class NodeManager {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String q = null;
|
String q = null;
|
||||||
|
StringBuffer b = new StringBuffer("SELECT count(*) FROM ").append(table);
|
||||||
|
|
||||||
if (home.getSubnodeRelation() != null) {
|
if (home.getSubnodeRelation() != null) {
|
||||||
// use the manually set subnoderelation of the home node
|
// use the manually set subnoderelation of the home node
|
||||||
q = new StringBuffer("SELECT count(*) FROM ").append(table).append(" ")
|
q = b.append(" ").append(home.getSubnodeRelation()).toString();
|
||||||
.append(home.getSubnodeRelation())
|
|
||||||
.toString();
|
|
||||||
} else {
|
} else {
|
||||||
// let relation object build the query
|
// let relation object build the query
|
||||||
q = new StringBuffer("SELECT count(*) FROM ").append(table)
|
q = b.append(rel.buildQuery(home,
|
||||||
.append(rel.buildQuery(home,
|
home.getNonVirtualParent(),
|
||||||
home.getNonVirtualParent(),
|
null,
|
||||||
null,
|
" WHERE ",
|
||||||
" WHERE ",
|
false)).toString();
|
||||||
false))
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logSql) {
|
if (logSql) {
|
||||||
|
@ -1408,15 +1400,19 @@ public final class NodeManager {
|
||||||
Statement stmt = null;
|
Statement stmt = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
StringBuffer q = new StringBuffer("SELECT ").append(namefield).append(" FROM ")
|
StringBuffer q = new StringBuffer("SELECT ").append(namefield)
|
||||||
.append(table);
|
.append(" FROM ")
|
||||||
|
.append(table);
|
||||||
|
|
||||||
if (home.getSubnodeRelation() != null) {
|
if (home.getSubnodeRelation() != null) {
|
||||||
q.append(" ").append(home.getSubnodeRelation());
|
q.append(" ").append(home.getSubnodeRelation());
|
||||||
} else {
|
} else {
|
||||||
// let relation object build the query
|
// let relation object build the query
|
||||||
q.append(rel.buildQuery(home, home.getNonVirtualParent(), null,
|
q.append(rel.buildQuery(home,
|
||||||
" WHERE ", true));
|
home.getNonVirtualParent(),
|
||||||
|
null,
|
||||||
|
" WHERE ",
|
||||||
|
true));
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt = con.createStatement();
|
stmt = con.createStatement();
|
||||||
|
@ -1477,13 +1473,11 @@ public final class NodeManager {
|
||||||
|
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
Relation[] joins = dbm.getJoins();
|
Relation[] joins = dbm.getJoins();
|
||||||
StringBuffer q = dbm.getSelect();
|
StringBuffer q = dbm.getSelect().append("WHERE ")
|
||||||
|
.append(dbm.getTableName())
|
||||||
q.append("WHERE ");
|
.append(".")
|
||||||
q.append(dbm.getTableName());
|
.append(idfield)
|
||||||
q.append(".");
|
.append(" = ");
|
||||||
q.append(idfield);
|
|
||||||
q.append(" = ");
|
|
||||||
|
|
||||||
if (dbm.needsQuotes(idfield)) {
|
if (dbm.needsQuotes(idfield)) {
|
||||||
q.append("'");
|
q.append("'");
|
||||||
|
@ -1578,8 +1572,11 @@ public final class NodeManager {
|
||||||
q.append(home.getSubnodeRelation().trim().substring(5));
|
q.append(home.getSubnodeRelation().trim().substring(5));
|
||||||
q.append(")");
|
q.append(")");
|
||||||
} else {
|
} else {
|
||||||
q.append(rel.buildQuery(home, home.getNonVirtualParent(), kstr,
|
q.append(rel.buildQuery(home,
|
||||||
"WHERE ", false));
|
home.getNonVirtualParent(),
|
||||||
|
kstr,
|
||||||
|
"WHERE ",
|
||||||
|
false));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logSql) {
|
if (logSql) {
|
||||||
|
@ -1916,12 +1913,14 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (cache) {
|
synchronized (cache) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
for (Enumeration en = add.elements(); en.hasMoreElements();) {
|
for (Enumeration en = add.elements(); en.hasMoreElements();) {
|
||||||
Node n = (Node) en.nextElement();
|
Node n = (Node) en.nextElement();
|
||||||
DbMapping dbm = app.getDbMapping(n.getPrototype());
|
DbMapping dbm = app.getDbMapping(n.getPrototype());
|
||||||
|
|
||||||
if (dbm != null) {
|
if (dbm != null) {
|
||||||
dbm.notifyDataChange();
|
dbm.setLastDataChange(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
n.lastParentSet = -1;
|
n.lastParentSet = -1;
|
||||||
|
@ -1937,7 +1936,7 @@ public final class NodeManager {
|
||||||
DbMapping dbm = app.getDbMapping(n.getPrototype());
|
DbMapping dbm = app.getDbMapping(n.getPrototype());
|
||||||
|
|
||||||
if (dbm != null) {
|
if (dbm != null) {
|
||||||
dbm.notifyDataChange();
|
dbm.setLastDataChange(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
n.setDbMapping(dbm);
|
n.setDbMapping(dbm);
|
||||||
|
|
|
@ -48,13 +48,17 @@ public final class Relation {
|
||||||
public final static int COMPLEX_REFERENCE = 3;
|
public final static int COMPLEX_REFERENCE = 3;
|
||||||
|
|
||||||
// constraints linked together by OR or AND if applicable?
|
// constraints linked together by OR or AND if applicable?
|
||||||
private int constraintsLogic;
|
public final static String AND = " AND ";
|
||||||
public final static int CONSTRAINTS_AND = 0;
|
public final static String OR = " OR ";
|
||||||
public final static int CONSTRAINTS_OR = 1;
|
public final static String XOR = " XOR ";
|
||||||
public final static int CONSTRAINTS_XOR = 2;
|
private String logicalOperator = AND;
|
||||||
public final String[] logicalOperators = {" AND ", " OR ", " XOR "};
|
|
||||||
|
// prefix to use for symbolic names of joined tables. The name is composed
|
||||||
// direct mapping is a very powerful feature: objects of some types can be directly accessed
|
// from this prefix and the name of the property we're doing the join for
|
||||||
|
final static String JOIN_PREFIX = "_JOIN_";
|
||||||
|
|
||||||
|
// direct mapping is a very powerful feature:
|
||||||
|
// objects of some types can be directly accessed
|
||||||
// by one of their properties/db fields.
|
// by one of their properties/db fields.
|
||||||
// public final static int DIRECT = 3;
|
// public final static int DIRECT = 3;
|
||||||
// the DbMapping of the type we come from
|
// the DbMapping of the type we come from
|
||||||
|
@ -85,22 +89,31 @@ public final class Relation {
|
||||||
String prototype;
|
String prototype;
|
||||||
String groupbyPrototype;
|
String groupbyPrototype;
|
||||||
String filter;
|
String filter;
|
||||||
|
String additionalTables;
|
||||||
int maxSize = 0;
|
int maxSize = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor makes a copy of an existing relation. Not all fields are copied, just those
|
* This constructor makes a copy of an existing relation. Not all fields are copied, just those
|
||||||
* which are needed in groupby- and virtual nodes defined by this relation.
|
* which are needed in groupby- and virtual nodes defined by this relation.
|
||||||
*/
|
*/
|
||||||
public Relation(Relation rel) {
|
private Relation(Relation rel) {
|
||||||
this.ownType = rel.ownType;
|
// Note: prototype, groupby, groupbyPrototype and groupbyOrder aren't copied here.
|
||||||
this.otherType = rel.otherType;
|
// these are set by the individual get*Relation() methods as appropriate.
|
||||||
this.propName = rel.propName;
|
this.ownType = rel.ownType;
|
||||||
this.columnName = rel.columnName;
|
this.otherType = rel.otherType;
|
||||||
this.reftype = rel.reftype;
|
this.propName = rel.propName;
|
||||||
this.constraints = rel.constraints;
|
this.columnName = rel.columnName;
|
||||||
this.accessName = rel.accessName;
|
this.reftype = rel.reftype;
|
||||||
this.maxSize = rel.maxSize;
|
this.order = rel.order;
|
||||||
this.constraintsLogic = rel.constraintsLogic;
|
this.filter = rel.filter;
|
||||||
|
this.additionalTables = rel.additionalTables;
|
||||||
|
this.maxSize = rel.maxSize;
|
||||||
|
this.constraints = rel.constraints;
|
||||||
|
this.accessName = rel.accessName;
|
||||||
|
this.maxSize = rel.maxSize;
|
||||||
|
this.logicalOperator = rel.logicalOperator;
|
||||||
|
this.aggressiveLoading = rel.aggressiveLoading;
|
||||||
|
this.aggressiveCaching = rel.aggressiveCaching;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -254,6 +267,14 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get additional tables
|
||||||
|
additionalTables = props.getProperty(propName + ".filter.additionalTables");
|
||||||
|
|
||||||
|
if (additionalTables != null) {
|
||||||
|
if (additionalTables.trim().length() == 0)
|
||||||
|
additionalTables = null;
|
||||||
|
}
|
||||||
|
|
||||||
// get max size of collection
|
// get max size of collection
|
||||||
String max = props.getProperty(propName + ".maxSize");
|
String max = props.getProperty(propName + ".maxSize");
|
||||||
|
|
||||||
|
@ -315,30 +336,24 @@ public final class Relation {
|
||||||
|
|
||||||
// parse constraints logic
|
// parse constraints logic
|
||||||
if (cnst.size() > 1) {
|
if (cnst.size() > 1) {
|
||||||
String logic = props.getProperty(propName + ".logic");
|
String logic = props.getProperty(propName + ".logicalOperator");
|
||||||
if ("and".equalsIgnoreCase(logic))
|
if ("and".equalsIgnoreCase(logic)) {
|
||||||
constraintsLogic = CONSTRAINTS_AND;
|
logicalOperator = AND;
|
||||||
else if ("or".equalsIgnoreCase(logic))
|
} else if ("or".equalsIgnoreCase(logic)) {
|
||||||
constraintsLogic = CONSTRAINTS_OR;
|
logicalOperator = OR;
|
||||||
else if ("xor".equalsIgnoreCase(logic))
|
} else if ("xor".equalsIgnoreCase(logic)) {
|
||||||
constraintsLogic = CONSTRAINTS_XOR;
|
logicalOperator = XOR;
|
||||||
else
|
} else {
|
||||||
throw new RuntimeException("Unrecognized logical operator: "+logic);
|
logicalOperator = AND;
|
||||||
} else
|
}
|
||||||
constraintsLogic = CONSTRAINTS_AND;
|
} else {
|
||||||
|
logicalOperator = AND;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
|
||||||
* Constraints linked by AND or OR?
|
|
||||||
*/
|
|
||||||
|
|
||||||
public String getConstraintsOperator() {
|
|
||||||
return logicalOperators[constraintsLogic];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this relation describe a virtual (collection) node?
|
* Does this relation describe a virtual (collection) node?
|
||||||
*/
|
*/
|
||||||
|
@ -579,12 +594,6 @@ public final class Relation {
|
||||||
vr.groupby = groupby;
|
vr.groupby = groupby;
|
||||||
vr.groupbyOrder = groupbyOrder;
|
vr.groupbyOrder = groupbyOrder;
|
||||||
vr.groupbyPrototype = groupbyPrototype;
|
vr.groupbyPrototype = groupbyPrototype;
|
||||||
vr.order = order;
|
|
||||||
vr.filter = filter;
|
|
||||||
vr.maxSize = maxSize;
|
|
||||||
vr.constraints = constraints;
|
|
||||||
vr.aggressiveLoading = aggressiveLoading;
|
|
||||||
vr.aggressiveCaching = aggressiveCaching;
|
|
||||||
|
|
||||||
return vr;
|
return vr;
|
||||||
}
|
}
|
||||||
|
@ -602,10 +611,6 @@ public final class Relation {
|
||||||
vr.groupby = groupby;
|
vr.groupby = groupby;
|
||||||
vr.groupbyOrder = groupbyOrder;
|
vr.groupbyOrder = groupbyOrder;
|
||||||
vr.groupbyPrototype = groupbyPrototype;
|
vr.groupbyPrototype = groupbyPrototype;
|
||||||
vr.order = order;
|
|
||||||
vr.filter = filter;
|
|
||||||
vr.maxSize = maxSize;
|
|
||||||
vr.constraints = constraints;
|
|
||||||
|
|
||||||
return vr;
|
return vr;
|
||||||
}
|
}
|
||||||
|
@ -620,13 +625,8 @@ public final class Relation {
|
||||||
|
|
||||||
Relation vr = new Relation(this);
|
Relation vr = new Relation(this);
|
||||||
|
|
||||||
vr.order = order;
|
|
||||||
vr.prototype = groupbyPrototype;
|
vr.prototype = groupbyPrototype;
|
||||||
vr.filter = filter;
|
|
||||||
vr.constraints = constraints;
|
|
||||||
vr.addConstraint(new Constraint(null, groupby, true));
|
vr.addConstraint(new Constraint(null, groupby, true));
|
||||||
vr.aggressiveLoading = aggressiveLoading;
|
|
||||||
vr.aggressiveCaching = aggressiveCaching;
|
|
||||||
|
|
||||||
return vr;
|
return vr;
|
||||||
}
|
}
|
||||||
|
@ -641,10 +641,7 @@ public final class Relation {
|
||||||
|
|
||||||
Relation vr = new Relation(this);
|
Relation vr = new Relation(this);
|
||||||
|
|
||||||
vr.order = order;
|
|
||||||
vr.prototype = groupbyPrototype;
|
vr.prototype = groupbyPrototype;
|
||||||
vr.filter = filter;
|
|
||||||
vr.constraints = constraints;
|
|
||||||
vr.addConstraint(new Constraint(null, groupby, true));
|
vr.addConstraint(new Constraint(null, groupby, true));
|
||||||
|
|
||||||
return vr;
|
return vr;
|
||||||
|
@ -681,7 +678,7 @@ public final class Relation {
|
||||||
prefix = " AND ";
|
prefix = " AND ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (constraints.length > 1 && constraintsLogic != CONSTRAINTS_AND) {
|
if (constraints.length > 1 && logicalOperator != AND) {
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
q.append("(");
|
q.append("(");
|
||||||
prefix = "";
|
prefix = "";
|
||||||
|
@ -690,10 +687,10 @@ public final class Relation {
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
constraints[i].addToQuery(q, home, nonvirtual);
|
constraints[i].addToQuery(q, home, nonvirtual);
|
||||||
prefix = getConstraintsOperator();
|
prefix = logicalOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (constraints.length > 1 && constraintsLogic != CONSTRAINTS_AND) {
|
if (constraints.length > 1 && logicalOperator != AND) {
|
||||||
q.append(")");
|
q.append(")");
|
||||||
prefix = " AND ";
|
prefix = " AND ";
|
||||||
}
|
}
|
||||||
|
@ -744,12 +741,17 @@ public final class Relation {
|
||||||
return q.toString();
|
return q.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the constraints for this relation for use within
|
||||||
|
* a left outer join select statement for the base object.
|
||||||
|
*/
|
||||||
public void renderJoinConstraints(StringBuffer select) {
|
public void renderJoinConstraints(StringBuffer select) {
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
select.append(ownType.getTableName());
|
select.append(ownType.getTableName());
|
||||||
select.append(".");
|
select.append(".");
|
||||||
select.append(constraints[i].localName);
|
select.append(constraints[i].localName);
|
||||||
select.append(" = _HLM_");
|
select.append(" = ");
|
||||||
|
select.append(JOIN_PREFIX);
|
||||||
select.append(propName);
|
select.append(propName);
|
||||||
select.append(".");
|
select.append(".");
|
||||||
select.append(constraints[i].foreignName);
|
select.append(constraints[i].foreignName);
|
||||||
|
|
Loading…
Add table
Reference in a new issue