* Implement bug #516
* Fix bug #515 * Some refactoring in helma.objectmodel.db
This commit is contained in:
parent
d8f5446d01
commit
1091d34c77
7 changed files with 319 additions and 196 deletions
|
@ -97,7 +97,7 @@ public final class DbColumn {
|
||||||
// Note: not sure if check for primitive or reference relation is really
|
// Note: not sure if check for primitive or reference relation is really
|
||||||
// needed, but we did it before, so we leave it in for safety.
|
// needed, but we did it before, so we leave it in for safety.
|
||||||
return isId || isPrototype || isName ||
|
return isId || isPrototype || isName ||
|
||||||
(relation != null && (relation.isPrimitive() || relation.isReference()));
|
(relation != null && relation.isPrimitiveOrReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -62,8 +62,8 @@ public final class DbMapping {
|
||||||
private HashMap prop2db;
|
private HashMap prop2db;
|
||||||
|
|
||||||
// Map of db columns to Relations objects.
|
// Map of db columns to Relations objects.
|
||||||
// Case insensitive, keys are stored in upper case so
|
// Case insensitive, keys are stored in lower case so
|
||||||
// lookups must do a toUpperCase().
|
// lookups must do a toLowerCase().
|
||||||
private HashMap db2prop;
|
private HashMap db2prop;
|
||||||
|
|
||||||
// list of columns to fetch from db
|
// list of columns to fetch from db
|
||||||
|
@ -303,24 +303,21 @@ public final class DbMapping {
|
||||||
// (ResourceProperties now preserve key capitalization!)
|
// (ResourceProperties now preserve key capitalization!)
|
||||||
p2d.put(propName.toLowerCase(), rel);
|
p2d.put(propName.toLowerCase(), rel);
|
||||||
|
|
||||||
if ((rel.columnName != null) &&
|
if ((rel.columnName != null) && rel.isPrimitiveOrReference()) {
|
||||||
((rel.reftype == Relation.PRIMITIVE) ||
|
Relation old = (Relation) d2p.put(rel.columnName.toLowerCase(), rel);
|
||||||
(rel.reftype == Relation.REFERENCE))) {
|
|
||||||
Relation old = (Relation) d2p.put(rel.columnName.toUpperCase(), rel);
|
|
||||||
// check if we're overwriting another relation
|
// check if we're overwriting another relation
|
||||||
// if so, primitive relations get precendence to references
|
// if so, primitive relations get precendence to references
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
app.logEvent("*** Duplicate mapping for "+typename+"."+rel.columnName);
|
app.logEvent("*** Duplicate mapping for " + typename + "." + rel.columnName);
|
||||||
if (old.reftype == Relation.PRIMITIVE) {
|
if (old.isPrimitive()) {
|
||||||
d2p.put(old.columnName.toUpperCase(), old);
|
d2p.put(old.columnName.toLowerCase(), old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if a reference is aggressively fetched
|
// check if a reference is aggressively fetched
|
||||||
if ((rel.reftype == Relation.REFERENCE ||
|
if (rel.aggressiveLoading &&
|
||||||
rel.reftype == Relation.COMPLEX_REFERENCE) &&
|
(rel.isReference() || rel.isComplexReference())) {
|
||||||
rel.aggressiveLoading) {
|
|
||||||
joinList.add(rel);
|
joinList.add(rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +410,9 @@ public final class DbMapping {
|
||||||
return parentMapping.getPrototypeName(id);
|
return parentMapping.getPrototypeName(id);
|
||||||
}
|
}
|
||||||
// fallback to base-prototype if the proto isn't recogniced
|
// fallback to base-prototype if the proto isn't recogniced
|
||||||
|
if (id == null) {
|
||||||
|
return typename;
|
||||||
|
}
|
||||||
return extensionMap.getProperty(id, typename);
|
return extensionMap.getProperty(id, typename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,7 +581,7 @@ public final class DbMapping {
|
||||||
columnName = columnName.substring(open + 1, close);
|
columnName = columnName.substring(open + 1, close);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _columnNameToProperty(columnName.toUpperCase());
|
return _columnNameToProperty(columnName.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String _columnNameToProperty(final String columnName) {
|
private String _columnNameToProperty(final String columnName) {
|
||||||
|
@ -591,9 +591,7 @@ public final class DbMapping {
|
||||||
return parentMapping._columnNameToProperty(columnName);
|
return parentMapping._columnNameToProperty(columnName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rel != null) &&
|
if ((rel != null) && rel.isPrimitiveOrReference()) {
|
||||||
((rel.reftype == Relation.PRIMITIVE) ||
|
|
||||||
(rel.reftype == Relation.REFERENCE))) {
|
|
||||||
return rel.propName;
|
return rel.propName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,15 +599,16 @@ public final class DbMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate an object property name to a database column name according to this mapping.
|
* Translate an object property name to a database column name according
|
||||||
|
* to this mapping. If no mapping is found, the property name is returned,
|
||||||
|
* assuming property and column names are equal.
|
||||||
*/
|
*/
|
||||||
public String propertyToColumnName(String propName) {
|
public String propertyToColumnName(String propName) {
|
||||||
if (propName == null) {
|
if (propName == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: prop2db stores keys in lower case, because it gets them
|
// prop2db stores keys in lower case
|
||||||
// from a SystemProperties object which converts keys to lower case.
|
|
||||||
return _propertyToColumnName(propName.toLowerCase());
|
return _propertyToColumnName(propName.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,9 +619,7 @@ public final class DbMapping {
|
||||||
return parentMapping._propertyToColumnName(propName);
|
return parentMapping._propertyToColumnName(propName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rel != null) &&
|
if ((rel != null) && (rel.isPrimitiveOrReference())) {
|
||||||
((rel.reftype == Relation.PRIMITIVE) ||
|
|
||||||
(rel.reftype == Relation.REFERENCE))) {
|
|
||||||
return rel.columnName;
|
return rel.columnName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,7 +634,7 @@ public final class DbMapping {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _columnNameToRelation(columnName.toUpperCase());
|
return _columnNameToRelation(columnName.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Relation _columnNameToRelation(final String columnName) {
|
private Relation _columnNameToRelation(final String columnName) {
|
||||||
|
@ -702,28 +699,6 @@ public final class DbMapping {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param propname ...
|
|
||||||
*
|
|
||||||
* @return ...
|
|
||||||
*/
|
|
||||||
public DbMapping getExactPropertyMapping(String propname) {
|
|
||||||
Relation rel = getExactPropertyRelation(propname);
|
|
||||||
|
|
||||||
if (rel != null) {
|
|
||||||
// if this is a virtual node, it doesn't have a dbmapping
|
|
||||||
if (rel.virtual && (rel.prototype == null)) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return rel.otherType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
|
|
@ -46,11 +46,31 @@ public final class MultiKey implements Key, Serializable {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* make a key for a persistent Object, describing its datasource and key parts.
|
* Make a key for a persistent Object, describing its datasource and key parts.
|
||||||
*/
|
*/
|
||||||
public MultiKey(DbMapping dbmap, Map parts) {
|
public MultiKey(DbMapping dbmap, Map parts) {
|
||||||
this.parts = parts;
|
this.parts = parts;
|
||||||
this.storageName = (dbmap == null) ? null : dbmap.getStorageTypeName();
|
this.storageName = getStorageNameFromParts(dbmap, parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the actual dbmapping prototype name out of the parts map if possible.
|
||||||
|
* This is necessary to implement references to unspecified prototype targets.
|
||||||
|
* @param dbmap the nominal/static dbmapping
|
||||||
|
* @param parts the parts map
|
||||||
|
* @return the actual dbmapping name
|
||||||
|
*/
|
||||||
|
private String getStorageNameFromParts(DbMapping dbmap, Map parts) {
|
||||||
|
if (dbmap == null)
|
||||||
|
return null;
|
||||||
|
String protoName = (String) parts.get("$prototype");
|
||||||
|
if (protoName != null) {
|
||||||
|
DbMapping dynamap = dbmap.app.getDbMapping(protoName);
|
||||||
|
if (dynamap != null) {
|
||||||
|
return (dynamap.getStorageTypeName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbmap.getStorageTypeName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package helma.objectmodel.db;
|
package helma.objectmodel.db;
|
||||||
|
|
||||||
import helma.framework.IPathElement;
|
import helma.framework.IPathElement;
|
||||||
|
import helma.framework.core.RequestEvaluator;
|
||||||
|
import helma.framework.core.Application;
|
||||||
import helma.objectmodel.ConcurrencyException;
|
import helma.objectmodel.ConcurrencyException;
|
||||||
import helma.objectmodel.INode;
|
import helma.objectmodel.INode;
|
||||||
import helma.objectmodel.IProperty;
|
import helma.objectmodel.IProperty;
|
||||||
|
@ -761,9 +763,7 @@ public final class Node implements INode, Serializable {
|
||||||
// see if there is an explicit relation defined for this parent info
|
// see if there is an explicit relation defined for this parent info
|
||||||
// we only try to fetch a node if an explicit relation is specified for the prop name
|
// we only try to fetch a node if an explicit relation is specified for the prop name
|
||||||
Relation rel = dbmap.propertyToRelation(pinfo.propname);
|
Relation rel = dbmap.propertyToRelation(pinfo.propname);
|
||||||
|
if ((rel != null) && (rel.isReference() || rel.isComplexReference())) {
|
||||||
if ((rel != null) && (rel.reftype == Relation.REFERENCE ||
|
|
||||||
rel.reftype == Relation.COMPLEX_REFERENCE)) {
|
|
||||||
pn = (Node) getNode(pinfo.propname);
|
pn = (Node) getNode(pinfo.propname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,10 +776,17 @@ public final class Node implements INode, Serializable {
|
||||||
if (pn != null) {
|
if (pn != null) {
|
||||||
// see if dbmapping specifies anonymity for this node
|
// see if dbmapping specifies anonymity for this node
|
||||||
if (pinfo.virtualname != null) {
|
if (pinfo.virtualname != null) {
|
||||||
pn = (Node) pn.getNode(pinfo.virtualname);
|
Node pn2 = (Node) pn.getNode(pinfo.virtualname);
|
||||||
if (pn == null)
|
if (pn2 == null) {
|
||||||
nmgr.nmgr.app.logError("Error: Can't retrieve parent node " +
|
getApp().logError("Error: Can't retrieve parent node " +
|
||||||
pinfo + " for " + this);
|
pinfo + " for " + this);
|
||||||
|
} else if (pn2.equals(this)) {
|
||||||
|
setParent(pn);
|
||||||
|
name = pinfo.virtualname;
|
||||||
|
anonymous = false;
|
||||||
|
return pn;
|
||||||
|
}
|
||||||
|
pn = pn2;
|
||||||
}
|
}
|
||||||
|
|
||||||
DbMapping dbm = (pn == null) ? null : pn.getDbMapping();
|
DbMapping dbm = (pn == null) ? null : pn.getDbMapping();
|
||||||
|
@ -798,7 +805,7 @@ public final class Node implements INode, Serializable {
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
nmgr.nmgr.app.logError("Error retrieving parent node " +
|
getApp().logError("Error retrieving parent node " +
|
||||||
pinfo + " for " + this, x);
|
pinfo + " for " + this, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -810,8 +817,8 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parentHandle == null && !nmgr.isRootNode(this) && state != TRANSIENT) {
|
if (parentHandle == null && !nmgr.isRootNode(this) && state != TRANSIENT) {
|
||||||
nmgr.nmgr.app.logEvent("*** Couldn't resolve parent for " + this);
|
getApp().logEvent("*** Couldn't resolve parent for " + this);
|
||||||
nmgr.nmgr.app.logEvent("*** Please check _parent info in type.properties!");
|
getApp().logEvent("*** Please check _parent info in type.properties!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1317,7 +1324,8 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Locally" remove a subnode from the subnodes table.
|
* "Locally" remove a subnode from the subnodes table.
|
||||||
* The logical stuff necessary for keeping data consistent is done in removeNode().
|
* The logical stuff necessary for keeping data consistent is done in
|
||||||
|
* {@link #removeNode(INode)}.
|
||||||
*/
|
*/
|
||||||
protected void releaseNode(Node node) {
|
protected void releaseNode(Node node) {
|
||||||
INode parent = node.getParent();
|
INode parent = node.getParent();
|
||||||
|
@ -1325,35 +1333,40 @@ public final class Node implements INode, Serializable {
|
||||||
checkWriteLock();
|
checkWriteLock();
|
||||||
node.checkWriteLock();
|
node.checkWriteLock();
|
||||||
|
|
||||||
boolean removed = false;
|
|
||||||
|
|
||||||
// load subnodes in case they haven't been loaded.
|
// load subnodes in case they haven't been loaded.
|
||||||
// this is to prevent subsequent access to reload the
|
// this is to prevent subsequent access to reload the
|
||||||
// index which would potentially still contain the removed child
|
// index which would potentially still contain the removed child
|
||||||
loadNodes();
|
loadNodes();
|
||||||
|
|
||||||
if (subnodes != null) {
|
if (subnodes != null) {
|
||||||
|
boolean removed = false;
|
||||||
synchronized (subnodes) {
|
synchronized (subnodes) {
|
||||||
removed = subnodes.remove(node.getHandle());
|
removed = subnodes.remove(node.getHandle());
|
||||||
}
|
}
|
||||||
|
if (removed) {
|
||||||
|
registerSubnodeChange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removed)
|
|
||||||
registerSubnodeChange();
|
|
||||||
|
|
||||||
// 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)) {
|
||||||
Relation prel = dbmap.getSubnodeRelation();
|
Relation prel = dbmap.getSubnodeRelation();
|
||||||
|
|
||||||
if ((prel != null) && (prel.accessName != null)) {
|
if (prel != null) {
|
||||||
Relation localrel = node.dbmap.columnNameToRelation(prel.accessName);
|
if (prel.accessName != null) {
|
||||||
|
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 : localrel.propName;
|
String propname = (localrel == null) ? prel.accessName : localrel.propName;
|
||||||
String prop = node.getString(propname);
|
String prop = node.getString(propname);
|
||||||
|
|
||||||
if ((prop != null) && (getNode(prop) == node)) {
|
if ((prop != null) && (getNode(prop) == node)) {
|
||||||
unset(prop);
|
unset(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prel.countConstraints() > 1) {
|
||||||
|
prel.unsetConstraints(this, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1370,7 +1383,6 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
lastmodified = System.currentTimeMillis();
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
// nmgr.logEvent ("released node "+node +" from "+this+" oldobj = "+what);
|
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
markAs(MODIFIED);
|
markAs(MODIFIED);
|
||||||
}
|
}
|
||||||
|
@ -2276,12 +2288,15 @@ public final class Node implements INode, Serializable {
|
||||||
public void setNode(String propname, INode value) {
|
public void setNode(String propname, INode value) {
|
||||||
// nmgr.logEvent ("setting node prop");
|
// nmgr.logEvent ("setting node prop");
|
||||||
// check if types match, otherwise throw exception
|
// check if types match, otherwise throw exception
|
||||||
DbMapping nmap = (dbmap == null) ? null : dbmap.getExactPropertyMapping(propname);
|
Relation rel = (dbmap == null) ?
|
||||||
|
null : dbmap.getExactPropertyRelation(propname);
|
||||||
|
DbMapping nmap = (rel == null) ? null : rel.getPropertyMapping();
|
||||||
|
DbMapping vmap = value.getDbMapping();
|
||||||
|
|
||||||
if ((nmap != null) && (nmap != value.getDbMapping())) {
|
if ((nmap != null) && (nmap != vmap)) {
|
||||||
if (value.getDbMapping() == null) {
|
if (vmap == null) {
|
||||||
value.setDbMapping(nmap);
|
value.setDbMapping(nmap);
|
||||||
} else if (!nmap.isStorageCompatible(value.getDbMapping())) {
|
} else if (!nmap.isStorageCompatible(vmap) && !rel.isComplexReference()) {
|
||||||
throw new RuntimeException("Can't set " + propname +
|
throw new RuntimeException("Can't set " + propname +
|
||||||
" to object with prototype " +
|
" to object with prototype " +
|
||||||
value.getPrototype() + ", was expecting " +
|
value.getPrototype() + ", was expecting " +
|
||||||
|
@ -2333,7 +2348,10 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
String p2 = propname.toLowerCase();
|
String p2 = propname.toLowerCase();
|
||||||
|
|
||||||
Relation rel = (dbmap == null) ? null : dbmap.getPropertyRelation(propname);
|
if (rel == null && dbmap != null) {
|
||||||
|
// widen relation to non-exact (collection) mapping
|
||||||
|
rel = dbmap.getPropertyRelation(propname);
|
||||||
|
}
|
||||||
|
|
||||||
if (rel != null && (rel.countConstraints() > 1 || rel.isComplexReference())) {
|
if (rel != null && (rel.countConstraints() > 1 || rel.isComplexReference())) {
|
||||||
rel.setConstraints(this, n);
|
rel.setConstraints(this, n);
|
||||||
|
@ -2367,7 +2385,7 @@ public final class Node implements INode, Serializable {
|
||||||
prop.setNodeValue(n);
|
prop.setNodeValue(n);
|
||||||
|
|
||||||
if ((rel == null) ||
|
if ((rel == null) ||
|
||||||
rel.reftype == Relation.REFERENCE ||
|
rel.isReference() ||
|
||||||
state == TRANSIENT ||
|
state == TRANSIENT ||
|
||||||
rel.otherType == null ||
|
rel.otherType == null ||
|
||||||
!rel.otherType.isRelational()) {
|
!rel.otherType.isRelational()) {
|
||||||
|
@ -2417,20 +2435,19 @@ public final class Node implements INode, Serializable {
|
||||||
* specified via property relation.
|
* specified via property relation.
|
||||||
*/
|
*/
|
||||||
public void unset(String propname) {
|
public void unset(String propname) {
|
||||||
if (propMap == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// if node is relational, leave a null property so that it is
|
// if node is relational, leave a null property so that it is
|
||||||
// updated in the DB. Otherwise, remove the property.
|
// updated in the DB. Otherwise, remove the property.
|
||||||
Property p;
|
Property p = null;
|
||||||
boolean relational = (dbmap != null) && dbmap.isRelational();
|
boolean relational = (dbmap != null) && dbmap.isRelational();
|
||||||
|
|
||||||
if (relational) {
|
if (propMap != null) {
|
||||||
p = (Property) propMap.get(propname.toLowerCase());
|
if (relational) {
|
||||||
} else {
|
p = (Property) propMap.get(propname.toLowerCase());
|
||||||
p = (Property) propMap.remove(propname.toLowerCase());
|
} else {
|
||||||
|
p = (Property) propMap.remove(propname.toLowerCase());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
|
@ -2456,7 +2473,8 @@ public final class Node implements INode, Serializable {
|
||||||
rel.unsetConstraints(this, p.getNodeValue());
|
rel.unsetConstraints(this, p.getNodeValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ignore) {
|
} catch (Exception x) {
|
||||||
|
getApp().logError("Error unsetting property", x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2479,11 +2497,23 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Return a string representation for this node. This tries to call the
|
||||||
*
|
* javascript implemented toString() if it is defined.
|
||||||
* @return ...
|
* @return a string representing this node.
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
try {
|
||||||
|
// We need to reach deap into helma.framework.core to invoke onInit(),
|
||||||
|
// but the functionality is really worth it.
|
||||||
|
RequestEvaluator reval = getApp().getCurrentRequestEvaluator();
|
||||||
|
if (reval != null) {
|
||||||
|
Object str = reval.invokeDirectFunction(this, "toString", RequestEvaluator.EMPTY_ARGS);
|
||||||
|
if (str instanceof String)
|
||||||
|
return (String) str;
|
||||||
|
}
|
||||||
|
} catch (Exception x) {
|
||||||
|
// fall back to default representation
|
||||||
|
}
|
||||||
return "HopObject " + name;
|
return "HopObject " + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2670,11 +2700,19 @@ public final class Node implements INode, Serializable {
|
||||||
// FIXME: what do we do if this.dbmap is null
|
// FIXME: what do we do if this.dbmap is null
|
||||||
if (this.dbmap == null) {
|
if (this.dbmap == null) {
|
||||||
throw new RuntimeException (this + " doesn't have a DbMapping");
|
throw new RuntimeException (this + " doesn't have a DbMapping");
|
||||||
}
|
}
|
||||||
Relation rel = this.dbmap.getSubnodeRelation();
|
Relation rel = this.dbmap.getSubnodeRelation();
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
lastSubnodeFetch = System.currentTimeMillis();
|
lastSubnodeFetch = System.currentTimeMillis();
|
||||||
return this.nmgr.updateSubnodeList(this, rel);
|
return this.nmgr.updateSubnodeList(this, rel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the application this node belongs to.
|
||||||
|
* @return the app we belong to
|
||||||
|
*/
|
||||||
|
private Application getApp() {
|
||||||
|
return nmgr.nmgr.app;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -112,7 +112,7 @@ public final class NodeManager {
|
||||||
* Checks if the given node is the application's root node.
|
* Checks if the given node is the application's root node.
|
||||||
*/
|
*/
|
||||||
public boolean isRootNode(Node node) {
|
public boolean isRootNode(Node node) {
|
||||||
return app.getRootId().equals(node.getID()) &&
|
return node.getState() != Node.TRANSIENT && app.getRootId().equals(node.getID()) &&
|
||||||
DbMapping.areStorageCompatible(app.getRootMapping(), node.getDbMapping());
|
DbMapping.areStorageCompatible(app.getRootMapping(), node.getDbMapping());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,11 +215,12 @@ public final class NodeManager {
|
||||||
Transactor tx = (Transactor) Thread.currentThread();
|
Transactor tx = (Transactor) Thread.currentThread();
|
||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
|
DbMapping otherDbm = rel == null ? null : rel.otherType;
|
||||||
// check what kind of object we're looking for and make an apropriate key
|
// check what kind of object we're looking for and make an apropriate key
|
||||||
if (rel.isComplexReference()) {
|
if (rel.isComplexReference()) {
|
||||||
// a key for a complex reference
|
// a key for a complex reference
|
||||||
key = new MultiKey(rel.otherType, rel.getKeyParts(home));
|
key = new MultiKey(rel.otherType, rel.getKeyParts(home));
|
||||||
|
otherDbm = app.getDbMapping(key.getStorageName());
|
||||||
} else if (rel.createOnDemand()) {
|
} else if (rel.createOnDemand()) {
|
||||||
// a key for a virtually defined object that's never actually stored in the db
|
// a key for a virtually defined object that's never actually stored in the db
|
||||||
// or a key for an object that represents subobjects grouped by some property,
|
// or a key for an object that represents subobjects grouped by some property,
|
||||||
|
@ -275,7 +276,7 @@ public final class NodeManager {
|
||||||
// The requested node isn't in the shared cache.
|
// The requested node isn't in the shared cache.
|
||||||
// Synchronize with key to make sure only one version is fetched
|
// Synchronize with key to make sure only one version is fetched
|
||||||
// from the database.
|
// from the database.
|
||||||
node = getNodeByRelation(tx.txn, home, kstr, rel);
|
node = getNodeByRelation(tx.txn, home, kstr, rel, otherDbm);
|
||||||
|
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
Node newNode = node;
|
Node newNode = node;
|
||||||
|
@ -433,7 +434,7 @@ public final class NodeManager {
|
||||||
* Insert a node into a different (relational) database than its default one.
|
* Insert a node into a different (relational) database than its default one.
|
||||||
*/
|
*/
|
||||||
public void exportNode(Node node, DbSource dbs)
|
public void exportNode(Node node, DbSource dbs)
|
||||||
throws IOException, SQLException, ClassNotFoundException {
|
throws SQLException, ClassNotFoundException {
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
throw new IllegalArgumentException("Node can't be null in exportNode");
|
throw new IllegalArgumentException("Node can't be null in exportNode");
|
||||||
}
|
}
|
||||||
|
@ -453,7 +454,7 @@ public final class NodeManager {
|
||||||
* Insert a node into a different (relational) database than its default one.
|
* Insert a node into a different (relational) database than its default one.
|
||||||
*/
|
*/
|
||||||
public void exportNode(Node node, DbMapping dbm)
|
public void exportNode(Node node, DbMapping dbm)
|
||||||
throws IOException, SQLException, ClassNotFoundException {
|
throws SQLException, ClassNotFoundException {
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
throw new IllegalArgumentException("Node can't be null in exportNode");
|
throw new IllegalArgumentException("Node can't be null in exportNode");
|
||||||
}
|
}
|
||||||
|
@ -593,7 +594,7 @@ public final class NodeManager {
|
||||||
|
|
||||||
// skip readonly, virtual and collection relations
|
// skip readonly, virtual and collection relations
|
||||||
if ((rel == null) || rel.readonly || rel.virtual ||
|
if ((rel == null) || rel.readonly || rel.virtual ||
|
||||||
(!rel.isReference() && !rel.isPrimitive())) {
|
(!rel.isPrimitiveOrReference())) {
|
||||||
// null out property so we don't consider it later
|
// null out property so we don't consider it later
|
||||||
props[i] = null;
|
props[i] = null;
|
||||||
continue;
|
continue;
|
||||||
|
@ -1568,7 +1569,7 @@ public final class NodeManager {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node getNodeByRelation(ITransaction txn, Node home, String kstr, Relation rel)
|
private Node getNodeByRelation(ITransaction txn, Node home, String kstr, Relation rel, DbMapping dbm)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Node node = null;
|
Node node = null;
|
||||||
|
|
||||||
|
@ -1582,25 +1583,22 @@ public final class NodeManager {
|
||||||
// set prototype and dbmapping on the newly created virtual/collection node
|
// set prototype and dbmapping on the newly created virtual/collection node
|
||||||
node.setPrototype(rel.prototype);
|
node.setPrototype(rel.prototype);
|
||||||
node.setDbMapping(rel.getVirtualMapping());
|
node.setDbMapping(rel.getVirtualMapping());
|
||||||
} else if ((rel != null) && (rel.groupby != null)) {
|
} else if (rel != null && rel.groupby != null) {
|
||||||
node = home.getGroupbySubnode(kstr, false);
|
node = home.getGroupbySubnode(kstr, false);
|
||||||
|
|
||||||
if ((node == null) &&
|
if (node == null && (dbm == null || !dbm.isRelational())) {
|
||||||
((rel.otherType == null) || !rel.otherType.isRelational())) {
|
|
||||||
node = (Node) db.getNode(txn, kstr);
|
node = (Node) db.getNode(txn, kstr);
|
||||||
node.nmgr = safe;
|
node.nmgr = safe;
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
} else if ((rel == null) || (rel.otherType == null) ||
|
} else if (rel == null || dbm == null || !dbm.isRelational()) {
|
||||||
!rel.otherType.isRelational()) {
|
|
||||||
node = (Node) db.getNode(txn, kstr);
|
node = (Node) db.getNode(txn, kstr);
|
||||||
node.nmgr = safe;
|
node.nmgr = safe;
|
||||||
node.setDbMapping(rel.otherType);
|
node.setDbMapping(dbm);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
} else {
|
} else {
|
||||||
DbMapping dbm = rel.otherType;
|
|
||||||
Statement stmt = null;
|
Statement stmt = null;
|
||||||
String query = null;
|
String query = null;
|
||||||
long logTimeStart = logSql ? System.currentTimeMillis() : 0;
|
long logTimeStart = logSql ? System.currentTimeMillis() : 0;
|
||||||
|
@ -1629,6 +1627,7 @@ public final class NodeManager {
|
||||||
} else {
|
} else {
|
||||||
b.append(rel.buildQuery(home,
|
b.append(rel.buildQuery(home,
|
||||||
home.getNonVirtualParent(),
|
home.getNonVirtualParent(),
|
||||||
|
dbm,
|
||||||
kstr,
|
kstr,
|
||||||
" WHERE ",
|
" WHERE ",
|
||||||
false));
|
false));
|
||||||
|
@ -1644,7 +1643,7 @@ public final class NodeManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = createNode(rel.otherType, rs, columns, 0);
|
node = createNode(dbm, rs, columns, 0);
|
||||||
|
|
||||||
fetchJoinedNodes(rs, joins, columns.length);
|
fetchJoinedNodes(rs, joins, columns.length);
|
||||||
|
|
||||||
|
@ -1855,8 +1854,7 @@ public final class NodeManager {
|
||||||
DbColumn[] columns2 = dbmap.getColumns();
|
DbColumn[] columns2 = dbmap.getColumns();
|
||||||
for (int i=0; i<columns2.length; i++) {
|
for (int i=0; i<columns2.length; i++) {
|
||||||
Relation rel = columns2[i].getRelation();
|
Relation rel = columns2[i].getRelation();
|
||||||
if (rel != null && (rel.reftype == Relation.PRIMITIVE ||
|
if (rel != null && rel.isPrimitiveOrReference()) {
|
||||||
rel.reftype == Relation.REFERENCE)) {
|
|
||||||
Property prop = (Property) propBuffer.get(columns2[i].getName());
|
Property prop = (Property) propBuffer.get(columns2[i].getName());
|
||||||
|
|
||||||
if (prop == null) {
|
if (prop == null) {
|
||||||
|
@ -1866,7 +1864,7 @@ public final class NodeManager {
|
||||||
prop.setName(rel.propName);
|
prop.setName(rel.propName);
|
||||||
|
|
||||||
// if the property is a pointer to another node, change the property type to NODE
|
// if the property is a pointer to another node, change the property type to NODE
|
||||||
if ((rel.reftype == Relation.REFERENCE) && rel.usesPrimaryKey()) {
|
if (rel.isReference() && rel.usesPrimaryKey()) {
|
||||||
// FIXME: References to anything other than the primary key are not supported
|
// FIXME: References to anything other than the primary key are not supported
|
||||||
prop.convertToNodeReference(rel.otherType);
|
prop.convertToNodeReference(rel.otherType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,15 +223,16 @@ public final class Relation {
|
||||||
} else {
|
} else {
|
||||||
boolean rprim = false;
|
boolean rprim = false;
|
||||||
for (int i=0; i<constraints.length; i++) {
|
for (int i=0; i<constraints.length; i++) {
|
||||||
if (constraints[0].foreignKeyIsPrimary()) {
|
if (constraints[i].foreignKeyIsPrimary()) {
|
||||||
rprim = true;
|
rprim = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
referencesPrimaryKey = rprim;
|
referencesPrimaryKey = rprim;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this is a non-trivial reference
|
// check if this is a non-trivial reference
|
||||||
if (constraints.length > 0 && !usesPrimaryKey()) {
|
if (constraints.length > 1 || !usesPrimaryKey()) {
|
||||||
reftype = COMPLEX_REFERENCE;
|
reftype = COMPLEX_REFERENCE;
|
||||||
} else {
|
} else {
|
||||||
reftype = REFERENCE;
|
reftype = REFERENCE;
|
||||||
|
@ -457,6 +458,14 @@ public final class Relation {
|
||||||
return reftype == REFERENCE;
|
return reftype == REFERENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this Relation describes either a primitive value
|
||||||
|
* or an object reference.
|
||||||
|
*/
|
||||||
|
public boolean isPrimitiveOrReference() {
|
||||||
|
return reftype == PRIMITIVE || reftype == REFERENCE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this Relation describes a collection.
|
* Returns true if this Relation describes a collection.
|
||||||
* <b>NOTE:</b> this will return true both for collection objects
|
* <b>NOTE:</b> this will return true both for collection objects
|
||||||
|
@ -742,6 +751,18 @@ public final class Relation {
|
||||||
return virtualMapping;
|
return virtualMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the db mapping for a propery relation.
|
||||||
|
* @return the target mapping of this property relation
|
||||||
|
*/
|
||||||
|
public DbMapping getPropertyMapping() {
|
||||||
|
// if this is an untyped virtual node, it doesn't have a dbmapping
|
||||||
|
if (!virtual || prototype != null) {
|
||||||
|
return otherType;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a Relation that defines the subnodes of a virtual node.
|
* Return a Relation that defines the subnodes of a virtual node.
|
||||||
*/
|
*/
|
||||||
|
@ -812,8 +833,18 @@ public final class Relation {
|
||||||
* Build the second half of an SQL select statement according to this relation
|
* Build the second half of an SQL select statement according to this relation
|
||||||
* and a local object.
|
* and a local object.
|
||||||
*/
|
*/
|
||||||
public String buildQuery(INode home, INode nonvirtual, String kstr, String pre,
|
public String buildQuery(INode home, INode nonvirtual,
|
||||||
boolean useOrder)
|
String kstr, String pre, boolean useOrder)
|
||||||
|
throws SQLException, ClassNotFoundException {
|
||||||
|
return buildQuery(home, nonvirtual, otherType, kstr, pre, useOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the second half of an SQL select statement according to this relation
|
||||||
|
* and a local object.
|
||||||
|
*/
|
||||||
|
public String buildQuery(INode home, INode nonvirtual, DbMapping otherDbm,
|
||||||
|
String kstr, String pre, boolean useOrder)
|
||||||
throws SQLException, ClassNotFoundException {
|
throws SQLException, ClassNotFoundException {
|
||||||
StringBuffer q = new StringBuffer();
|
StringBuffer q = new StringBuffer();
|
||||||
String prefix = pre;
|
String prefix = pre;
|
||||||
|
@ -822,14 +853,14 @@ public final class Relation {
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
|
|
||||||
String accessColumn = (accessName == null) ?
|
String accessColumn = (accessName == null) ?
|
||||||
otherType.getIDField() : accessName;
|
otherDbm.getIDField() : accessName;
|
||||||
otherType.appendCondition(q, accessColumn, kstr);
|
otherDbm.appendCondition(q, accessColumn, kstr);
|
||||||
|
|
||||||
prefix = " AND ";
|
prefix = " AND ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// render the constraints and filter
|
// render the constraints and filter
|
||||||
renderConstraints(q, home, nonvirtual, prefix);
|
renderConstraints(q, home, nonvirtual, otherDbm, prefix);
|
||||||
|
|
||||||
// add joined fetch constraints
|
// add joined fetch constraints
|
||||||
ownType.addJoinConstraints(q, prefix);
|
ownType.addJoinConstraints(q, prefix);
|
||||||
|
@ -906,17 +937,36 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render contraints and filter conditions to an SQL query string buffer
|
* Render contraints and filter conditions to an SQL query string buffer.
|
||||||
*
|
*
|
||||||
* @param q the query string
|
* @param q the query string
|
||||||
* @param home our home node
|
* @param home our home node
|
||||||
* @param nonvirtual our non-virtual home node
|
* @param nonvirtual our non-virtual home node
|
||||||
* @param prefix the prefix to use to append to the existing query (e.g. " AND ")
|
* @param prefix the prefix to use to append to the existing query (e.g. " AND ")
|
||||||
*
|
*
|
||||||
* @throws SQLException ...
|
* @throws SQLException sql related exception
|
||||||
|
* @throws ClassNotFoundException driver class not found
|
||||||
*/
|
*/
|
||||||
public void renderConstraints(StringBuffer q, INode home,
|
public void renderConstraints(StringBuffer q, INode home, INode nonvirtual,
|
||||||
INode nonvirtual, String prefix)
|
String prefix)
|
||||||
|
throws SQLException, ClassNotFoundException {
|
||||||
|
renderConstraints(q, home, nonvirtual, otherType, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render contraints and filter conditions to an SQL query string buffer.
|
||||||
|
*
|
||||||
|
* @param q the query string
|
||||||
|
* @param home our home node
|
||||||
|
* @param nonvirtual our non-virtual home nod
|
||||||
|
* @param otherDbm the DbMapping of the remote Node
|
||||||
|
* @param prefix the prefix to use to append to the existing query (e.g. " AND ")
|
||||||
|
*
|
||||||
|
* @throws SQLException sql related exception
|
||||||
|
* @throws ClassNotFoundException driver class not found
|
||||||
|
*/
|
||||||
|
public void renderConstraints(StringBuffer q, INode home, INode nonvirtual,
|
||||||
|
DbMapping otherDbm, String prefix)
|
||||||
throws SQLException, ClassNotFoundException {
|
throws SQLException, ClassNotFoundException {
|
||||||
|
|
||||||
if (constraints.length > 1 && logicalOperator != AND) {
|
if (constraints.length > 1 && logicalOperator != AND) {
|
||||||
|
@ -926,8 +976,13 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
if (constraints[i].foreignKeyIsPrototype()) {
|
||||||
|
// if foreign key is $prototype we already have this constraint
|
||||||
|
// covered by doing the select on the proper table
|
||||||
|
continue;
|
||||||
|
}
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
constraints[i].addToQuery(q, home, nonvirtual);
|
constraints[i].addToQuery(q, home, nonvirtual, otherDbm);
|
||||||
prefix = logicalOperator;
|
prefix = logicalOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,15 +995,15 @@ public final class Relation {
|
||||||
// specifies an extension of an prototype inside the brakets of
|
// specifies an extension of an prototype inside the brakets of
|
||||||
// a type.properties's collection, only nodes having this proto
|
// a type.properties's collection, only nodes having this proto
|
||||||
// sould appear inside the collection
|
// sould appear inside the collection
|
||||||
if (otherType.inheritsStorage()) {
|
if (otherDbm.inheritsStorage()) {
|
||||||
String protoField = otherType.getPrototypeField();
|
String protoField = otherDbm.getPrototypeField();
|
||||||
String[] extensions = otherType.getExtensions();
|
String[] extensions = otherDbm.getExtensions();
|
||||||
|
|
||||||
// extensions should never be null for extension- and
|
// extensions should never be null for extension- and
|
||||||
// extended prototypes. nevertheless we check it here
|
// extended prototypes. nevertheless we check it here
|
||||||
if (extensions != null && protoField != null) {
|
if (extensions != null && protoField != null) {
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
otherType.appendCondition(q, protoField, extensions);
|
otherDbm.appendCondition(q, protoField, extensions);
|
||||||
prefix = " AND ";
|
prefix = " AND ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -969,12 +1024,12 @@ public final class Relation {
|
||||||
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].localKey);
|
||||||
select.append(" = ");
|
select.append(" = ");
|
||||||
select.append(JOIN_PREFIX);
|
select.append(JOIN_PREFIX);
|
||||||
select.append(propName);
|
select.append(propName);
|
||||||
select.append(".");
|
select.append(".");
|
||||||
select.append(constraints[i].foreignName);
|
select.append(constraints[i].foreignKey);
|
||||||
if (isOracle) {
|
if (isOracle) {
|
||||||
// create old oracle style join - see
|
// create old oracle style join - see
|
||||||
// http://www.praetoriate.com/oracle_tips_outer_joins.htm
|
// http://www.praetoriate.com/oracle_tips_outer_joins.htm
|
||||||
|
@ -1034,21 +1089,28 @@ public final class Relation {
|
||||||
int satisfied = 0;
|
int satisfied = 0;
|
||||||
|
|
||||||
INode nonvirtual = parent.getNonVirtualParent();
|
INode nonvirtual = parent.getNonVirtualParent();
|
||||||
|
DbMapping otherDbm = child.getDbMapping();
|
||||||
|
if (otherDbm == null) {
|
||||||
|
otherDbm = otherType;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
String propname = constraints[i].foreignProperty();
|
Constraint cnst = constraints[i];
|
||||||
|
String propname = cnst.foreignProperty(otherDbm);
|
||||||
|
|
||||||
if (propname != null) {
|
if (propname != null) {
|
||||||
INode home = constraints[i].isGroupby ? parent
|
INode home = cnst.isGroupby ? parent
|
||||||
: nonvirtual;
|
: nonvirtual;
|
||||||
String value = null;
|
String value = null;
|
||||||
|
|
||||||
if (constraints[i].localKeyIsPrimary(home.getDbMapping())) {
|
if (cnst.localKeyIsPrimary(home.getDbMapping())) {
|
||||||
value = home.getID();
|
value = home.getID();
|
||||||
|
} else if (cnst.localKeyIsPrototype()) {
|
||||||
|
value = home.getDbMapping().getStorageTypeName();
|
||||||
} else if (ownType.isRelational()) {
|
} else if (ownType.isRelational()) {
|
||||||
value = home.getString(constraints[i].localProperty());
|
value = home.getString(cnst.localProperty());
|
||||||
} else {
|
} else {
|
||||||
value = home.getString(constraints[i].localName);
|
value = home.getString(cnst.localKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
|
@ -1084,32 +1146,40 @@ public final class Relation {
|
||||||
Node home = parent.getNonVirtualParent();
|
Node home = parent.getNonVirtualParent();
|
||||||
|
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
Constraint cnst = constraints[i];
|
||||||
// don't set groupby constraints since we don't know if the
|
// don't set groupby constraints since we don't know if the
|
||||||
// parent node is the base node or a group node
|
// parent node is the base node or a group node
|
||||||
if (constraints[i].isGroupby) {
|
if (cnst.isGroupby) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we update the local or the other object, depending on
|
// check if we update the local or the other object, depending on
|
||||||
// whether the primary key of either side is used.
|
// whether the primary key of either side is used.
|
||||||
|
boolean foreignIsPrimary = cnst.foreignKeyIsPrimary();
|
||||||
if (constraints[i].foreignKeyIsPrimary()) {
|
if (foreignIsPrimary || cnst.foreignKeyIsPrototype()) {
|
||||||
String localProp = constraints[i].localProperty();
|
String localProp = cnst.localProperty();
|
||||||
if (localProp == null) {
|
if (localProp == null) {
|
||||||
System.err.println ("Error: column "+constraints[i].localName+
|
ownType.app.logError("Error: column " + cnst.localKey +
|
||||||
" must be mapped in order to be used as constraint in "+
|
" must be mapped in order to be used as constraint in "+
|
||||||
Relation.this);
|
Relation.this);
|
||||||
} else {
|
} else {
|
||||||
home.setString(localProp, child.getID());
|
String value = foreignIsPrimary ?
|
||||||
|
child.getID() : child.getDbMapping().getStorageTypeName();
|
||||||
|
home.setString(localProp, value);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Relation crel = otherType.columnNameToRelation(constraints[i].foreignName);
|
DbMapping otherDbm = child.getDbMapping();
|
||||||
if (crel != null) {
|
if (otherDbm == null) {
|
||||||
// INode home = constraints[i].isGroupby ? parent : nonVirtual;
|
otherDbm = otherType;
|
||||||
|
}
|
||||||
|
|
||||||
if (constraints[i].localKeyIsPrimary(home.getDbMapping())) {
|
Relation crel = otherDbm.columnNameToRelation(cnst.foreignKey);
|
||||||
|
|
||||||
|
if (crel != null) {
|
||||||
|
|
||||||
|
if (cnst.localKeyIsPrimary(home.getDbMapping())) {
|
||||||
// only set node if property in child object is defined as reference.
|
// only set node if property in child object is defined as reference.
|
||||||
if (crel.reftype == REFERENCE) {
|
if (crel.reftype == REFERENCE) {
|
||||||
INode currentValue = child.getNode(crel.propName);
|
INode currentValue = child.getNode(crel.propName);
|
||||||
|
@ -1131,16 +1201,13 @@ public final class Relation {
|
||||||
child.setString(crel.propName, home.getID());
|
child.setString(crel.propName, home.getID());
|
||||||
}
|
}
|
||||||
} else if (crel.reftype == PRIMITIVE) {
|
} else if (crel.reftype == PRIMITIVE) {
|
||||||
Property prop = null;
|
if (cnst.localKeyIsPrototype()) {
|
||||||
|
child.setString(crel.propName, home.getDbMapping().getStorageTypeName());
|
||||||
if (ownType.isRelational()) {
|
|
||||||
prop = home.getProperty(constraints[i].localProperty());
|
|
||||||
} else {
|
} else {
|
||||||
prop = home.getProperty(constraints[i].localName);
|
Property prop = home.getProperty(cnst.localProperty());
|
||||||
}
|
if (prop != null) {
|
||||||
|
child.set(crel.propName, prop.getValue(), prop.getType());
|
||||||
if (prop != null) {
|
}
|
||||||
child.set(crel.propName, prop.getValue(), prop.getType());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1154,28 +1221,33 @@ public final class Relation {
|
||||||
Node home = parent.getNonVirtualParent();
|
Node home = parent.getNonVirtualParent();
|
||||||
|
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
Constraint cnst = constraints[i];
|
||||||
// don't set groupby constraints since we don't know if the
|
// don't set groupby constraints since we don't know if the
|
||||||
// parent node is the base node or a group node
|
// parent node is the base node or a group node
|
||||||
if (constraints[i].isGroupby) {
|
if (cnst.isGroupby) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we update the local or the other object, depending on
|
// check if we update the local or the other object, depending on
|
||||||
// whether the primary key of either side is used.
|
// whether the primary key of either side is used.
|
||||||
|
|
||||||
if (constraints[i].foreignKeyIsPrimary()) {
|
if (cnst.foreignKeyIsPrimary() || cnst.foreignKeyIsPrototype()) {
|
||||||
String localProp = constraints[i].localProperty();
|
String localProp = cnst.localProperty();
|
||||||
if (localProp != null) {
|
if (localProp != null) {
|
||||||
home.setString(localProp, null);
|
home.setString(localProp, null);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Relation crel = otherType.columnNameToRelation(constraints[i].foreignName);
|
DbMapping otherDbm = child.getDbMapping();
|
||||||
if (crel != null) {
|
if (otherDbm == null) {
|
||||||
// INode home = constraints[i].isGroupby ? parent : nonVirtual;
|
otherDbm = otherType;
|
||||||
|
}
|
||||||
|
|
||||||
if (constraints[i].localKeyIsPrimary(home.getDbMapping())) {
|
Relation crel = otherDbm.columnNameToRelation(cnst.foreignKey);
|
||||||
|
|
||||||
|
if (crel != null) {
|
||||||
|
if (cnst.localKeyIsPrimary(home.getDbMapping())) {
|
||||||
// only set node if property in child object is defined as reference.
|
// only set node if property in child object is defined as reference.
|
||||||
if (crel.reftype == REFERENCE) {
|
if (crel.reftype == REFERENCE) {
|
||||||
INode currentValue = child.getNode(crel.propName);
|
INode currentValue = child.getNode(crel.propName);
|
||||||
|
@ -1187,17 +1259,7 @@ public final class Relation {
|
||||||
child.setString(crel.propName, null);
|
child.setString(crel.propName, null);
|
||||||
}
|
}
|
||||||
} else if (crel.reftype == PRIMITIVE) {
|
} else if (crel.reftype == PRIMITIVE) {
|
||||||
Property prop = null;
|
child.setString(crel.propName, null);
|
||||||
|
|
||||||
if (ownType.isRelational()) {
|
|
||||||
prop = home.getProperty(constraints[i].localProperty());
|
|
||||||
} else {
|
|
||||||
prop = home.getProperty(constraints[i].localName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prop != null) {
|
|
||||||
child.setString(crel.propName, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1209,10 +1271,13 @@ public final class Relation {
|
||||||
public Map getKeyParts(INode home) {
|
public Map getKeyParts(INode home) {
|
||||||
Map map = new HashMap();
|
Map map = new HashMap();
|
||||||
for (int i=0; i<constraints.length; i++) {
|
for (int i=0; i<constraints.length; i++) {
|
||||||
if (ownType.getIDField().equalsIgnoreCase(constraints[i].localName)) {
|
Constraint cnst = constraints[i];
|
||||||
map.put(constraints[i].foreignName, home.getID());
|
if (cnst.localKeyIsPrimary(ownType)) {
|
||||||
|
map.put(cnst.foreignKey, home.getID());
|
||||||
|
} else if (cnst.localKeyIsPrototype()) {
|
||||||
|
map.put(cnst.foreignKey, home.getDbMapping().getStorageTypeName());
|
||||||
} else {
|
} else {
|
||||||
map.put(constraints[i].foreignName, home.getString(constraints[i].localProperty()));
|
map.put(cnst.foreignKey, home.getString(cnst.localProperty()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
|
@ -1246,53 +1311,77 @@ public final class Relation {
|
||||||
* establish a relation between database mapped objects.
|
* establish a relation between database mapped objects.
|
||||||
*/
|
*/
|
||||||
class Constraint {
|
class Constraint {
|
||||||
String localName;
|
String localKey;
|
||||||
String foreignName;
|
String foreignKey;
|
||||||
boolean isGroupby;
|
boolean isGroupby;
|
||||||
|
|
||||||
Constraint(String local, String foreign, boolean groupby) {
|
Constraint(String local, String foreign, boolean groupby) {
|
||||||
localName = local;
|
localKey = local;
|
||||||
foreignName = foreign;
|
foreignKey = foreign;
|
||||||
isGroupby = groupby;
|
isGroupby = groupby;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addToQuery(StringBuffer q, INode home, INode nonvirtual)
|
public void addToQuery(StringBuffer q, INode home, INode nonvirtual, DbMapping otherDbm)
|
||||||
throws SQLException, ClassNotFoundException {
|
throws SQLException, ClassNotFoundException {
|
||||||
String local = null;
|
String local;
|
||||||
INode ref = isGroupby ? home : nonvirtual;
|
INode ref = isGroupby ? home : nonvirtual;
|
||||||
|
|
||||||
if ((localName == null) ||
|
if (localKeyIsPrimary(ref.getDbMapping())) {
|
||||||
localName.equalsIgnoreCase(ref.getDbMapping().getIDField())) {
|
|
||||||
local = ref.getID();
|
local = ref.getID();
|
||||||
|
} else if (localKeyIsPrototype()) {
|
||||||
|
local = ref.getDbMapping().getStorageTypeName();
|
||||||
} else {
|
} else {
|
||||||
String homeprop = ownType.columnNameToProperty(localName);
|
String homeprop = ownType.columnNameToProperty(localKey);
|
||||||
|
if (homeprop == null) {
|
||||||
|
throw new SQLException("Invalid local name '" + localKey +
|
||||||
|
"' on " + ownType);
|
||||||
|
}
|
||||||
local = ref.getString(homeprop);
|
local = ref.getString(homeprop);
|
||||||
}
|
}
|
||||||
|
|
||||||
otherType.appendCondition(q, foreignName, local);
|
String columnName;
|
||||||
|
if (foreignKeyIsPrimary()) {
|
||||||
|
columnName = otherDbm.getIDField();
|
||||||
|
} else {
|
||||||
|
columnName = foreignKey;
|
||||||
|
}
|
||||||
|
otherDbm.appendCondition(q, columnName, local);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean foreignKeyIsPrimary() {
|
public boolean foreignKeyIsPrimary() {
|
||||||
return (foreignName == null) ||
|
return (foreignKey == null) ||
|
||||||
foreignName.equalsIgnoreCase(otherType.getIDField());
|
"$id".equalsIgnoreCase(foreignKey) ||
|
||||||
|
foreignKey.equalsIgnoreCase(otherType.getIDField());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean foreignKeyIsPrototype() {
|
||||||
|
return "$prototype".equalsIgnoreCase(foreignKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean localKeyIsPrimary(DbMapping homeMapping) {
|
public boolean localKeyIsPrimary(DbMapping homeMapping) {
|
||||||
return (homeMapping == null) || (localName == null) ||
|
return (homeMapping == null) || (localKey == null) ||
|
||||||
localName.equalsIgnoreCase(homeMapping.getIDField());
|
"$id".equalsIgnoreCase(localKey) ||
|
||||||
|
localKey.equalsIgnoreCase(homeMapping.getIDField());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String foreignProperty() {
|
public boolean localKeyIsPrototype() {
|
||||||
return otherType.columnNameToProperty(foreignName);
|
return "$prototype".equalsIgnoreCase(localKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String foreignProperty(DbMapping otherDbm) {
|
||||||
|
if (otherDbm.isRelational())
|
||||||
|
return otherDbm.columnNameToProperty(foreignKey);
|
||||||
|
return foreignKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String localProperty() {
|
public String localProperty() {
|
||||||
return ownType.columnNameToProperty(localName);
|
if (ownType.isRelational())
|
||||||
|
return ownType.columnNameToProperty(localKey);
|
||||||
|
return localKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return localName + "=" + otherType.getTypeName() + "." + foreignName;
|
return localKey + "=" + otherType.getTypeName() + "." + foreignKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
|
||||||
* @return the default value for the object
|
* @return the default value for the object
|
||||||
*/
|
*/
|
||||||
public Object getDefaultValue(Class hint) {
|
public Object getDefaultValue(Class hint) {
|
||||||
return toString();
|
return node == null ? toString() : node.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -981,12 +981,15 @@ public class HopObject extends ScriptableObject implements Wrapper, PropertyReco
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a string representation of this object
|
* Return a string representation of this HopObject.
|
||||||
*
|
* @return a string representing this HopObject
|
||||||
* @return ...
|
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return (className != null) ? ("[HopObject " + className + "]") : "[HopObject]";
|
if (node == null) {
|
||||||
|
return "[HopObject prototype " + className + "]";
|
||||||
|
} else {
|
||||||
|
return "[HopObject " + node.getName() + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue