Major cleanup all over the helma.objectmodel.db package.

Fixes bug 151.
Some optimization in getElementName().
Removed cases where user prototype was treated as a special case.
This commit is contained in:
hns 2002-11-22 19:26:39 +00:00
parent b052ee89a7
commit 83d997477c
5 changed files with 158 additions and 132 deletions

View file

@ -26,8 +26,6 @@ public final class DbMapping implements Updatable {
// prototype name of this mapping
String typename;
// int version;
// properties from where the mapping is read
SystemProperties props;
@ -42,8 +40,8 @@ public final class DbMapping implements Updatable {
ParentInfo[] parent;
// Relations describing subnodes and properties.
Relation subnodesRel;
Relation propertiesRel;
Relation subRelation;
Relation propRelation;
// if this defines a subnode mapping with groupby layer, we need a DbMapping for those groupby nodes
DbMapping groupbyMapping;
@ -230,17 +228,19 @@ public final class DbMapping implements Updatable {
if (subnodeMapping != null) {
try {
// check if subnode relation already exists. If so, reuse it
if (subnodesRel == null)
subnodesRel = new Relation (subnodeMapping, "_children", this, props);
subnodesRel.update (subnodeMapping, props);
if (subnodesRel.accessor != null)
propertiesRel = subnodesRel;
if (subRelation == null)
subRelation = new Relation (subnodeMapping, "_children", this, props);
subRelation.update (subnodeMapping, props);
// if subnodes are accessed via access name or group name,
// the subnode relation is also the property relation.
if (subRelation.accessor != null || subRelation.groupby != null)
propRelation = subRelation;
} catch (Exception x) {
app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ());
// subnodesRel = null;
// subRelation = null;
}
} else {
subnodesRel = propertiesRel = null;
subRelation = propRelation = null;
}
if (groupbyMapping != null) {
@ -422,8 +422,8 @@ public final class DbMapping implements Updatable {
public DbMapping getSubnodeMapping () {
if (subnodesRel != null)
return subnodesRel.otherType;
if (subRelation != null)
return subRelation.otherType;
if (parentMapping != null)
return parentMapping.getSubnodeMapping ();
return null;
@ -452,7 +452,7 @@ public final class DbMapping implements Updatable {
* db-mapping with the right relations to create the group-by nodes
*/
public synchronized DbMapping getGroupbyMapping () {
if (subnodesRel == null || subnodesRel.groupby == null)
if (subRelation == null || subRelation.groupby == null)
return null;
if (groupbyMapping == null) {
initGroupbyMapping ();
@ -469,38 +469,31 @@ public final class DbMapping implements Updatable {
groupbyMapping = new DbMapping (app);
// If a mapping is defined, make the internal mapping inherit from
// the defined named prototype.
if (subnodesRel.groupbyprototype != null)
groupbyMapping.parentMapping = app.getDbMapping (subnodesRel.groupbyprototype);
groupbyMapping.subnodesRel = subnodesRel.getGroupbySubnodeRelation ();
if (propertiesRel != null)
groupbyMapping.propertiesRel = propertiesRel.getGroupbyPropertyRelation ();
if (subRelation.groupbyPrototype != null)
groupbyMapping.parentMapping = app.getDbMapping (subRelation.groupbyPrototype);
groupbyMapping.subRelation = subRelation.getGroupbySubnodeRelation ();
if (propRelation != null)
groupbyMapping.propRelation = propRelation.getGroupbyPropertyRelation ();
else
groupbyMapping.propertiesRel = subnodesRel.getGroupbyPropertyRelation ();
groupbyMapping.typename = subnodesRel.groupbyprototype;
groupbyMapping.propRelation = subRelation.getGroupbyPropertyRelation ();
groupbyMapping.typename = subRelation.groupbyPrototype;
}
/* public void setPropertyMapping (DbMapping pm) {
properties = pm;
} */
/* public void setSubnodeRelation (Relation rel) {
subnodesRel = rel;
} */
public void setPropertyRelation (Relation rel) {
propertiesRel = rel;
propRelation = rel;
}
public Relation getSubnodeRelation () {
if (subnodesRel == null && parentMapping != null)
if (subRelation == null && parentMapping != null)
return parentMapping.getSubnodeRelation ();
return subnodesRel;
return subRelation;
}
public Relation getPropertyRelation () {
if (propertiesRel == null && parentMapping != null)
if (propRelation == null && parentMapping != null)
return parentMapping.getPropertyRelation ();
return propertiesRel;
return propRelation;
}
public Relation getPropertyRelation (String propname) {
@ -524,9 +517,9 @@ public final class DbMapping implements Updatable {
}
public String getSubnodeGroupby () {
if (subnodesRel == null && parentMapping != null)
if (subRelation == null && parentMapping != null)
return parentMapping.getSubnodeGroupby ();
return subnodesRel == null ? null : subnodesRel.groupby;
return subRelation == null ? null : subRelation.groupby;
}
public String getIDgen () {

View file

@ -17,7 +17,6 @@ import helma.objectmodel.*;
import helma.util.*;
import helma.framework.IPathElement;
import java.math.BigDecimal;
// import com.workingdogs.village.*;
/**
@ -154,7 +153,7 @@ public final class Node implements INode, Serializable {
transient String subnodeRelation = null;
transient long lastSubnodeFetch = 0;
transient long lastSubnodeChange = 0;
transient long lastNameCheck = 0;
transient long lastParentSet = 0;
transient long lastSubnodeCount = 0; // these two are only used
@ -261,11 +260,6 @@ public final class Node implements INode, Serializable {
name = nameField == null ? id : rs.getString (nameField);
if (name == null || name.length() == 0)
name = dbmap.getTypeName() + " " + id;
// set parent for user objects to internal userroot node
if ("user".equals (prototype)) {
parentHandle = new NodeHandle (new DbKey (null, "1"));
anonymous = false;
}
created = lastmodified = System.currentTimeMillis ();
@ -281,25 +275,25 @@ public final class Node implements INode, Serializable {
switch (rel.getColumnType()) {
case Types.BIT:
newprop.setBooleanValue (rs.getBoolean(rel.getDbField()));
newprop.setBooleanValue (rs.getBoolean(columns[i]));
break;
case Types.TINYINT:
case Types.BIGINT:
case Types.SMALLINT:
case Types.INTEGER:
newprop.setIntegerValue (rs.getLong(rel.getDbField()));
newprop.setIntegerValue (rs.getLong(columns[i]));
break;
case Types.REAL:
case Types.FLOAT:
case Types.DOUBLE:
newprop.setFloatValue (rs.getDouble(rel.getDbField()));
newprop.setFloatValue (rs.getDouble(columns[i]));
break;
case Types.DECIMAL:
case Types.NUMERIC:
BigDecimal num = rs.getBigDecimal (rel.getDbField());
BigDecimal num = rs.getBigDecimal (columns[i]);
if (num == null)
break;
if (num.scale() > 0)
@ -311,20 +305,20 @@ public final class Node implements INode, Serializable {
case Types.LONGVARBINARY:
case Types.VARBINARY:
case Types.BINARY:
newprop.setStringValue (rs.getString(rel.getDbField()));
newprop.setStringValue (rs.getString(columns[i]));
break;
case Types.LONGVARCHAR:
case Types.CHAR:
case Types.VARCHAR:
case Types.OTHER:
newprop.setStringValue (rs.getString(rel.getDbField()));
newprop.setStringValue (rs.getString(columns[i]));
break;
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
newprop.setDateValue (rs.getTimestamp(rel.getDbField()));
newprop.setDateValue (rs.getTimestamp(columns[i]));
break;
case Types.NULL:
@ -333,7 +327,7 @@ public final class Node implements INode, Serializable {
// continue;
default:
newprop.setStringValue (rs.getString(rel.getDbField()));
newprop.setStringValue (rs.getString(columns[i]));
break;
}
@ -484,10 +478,7 @@ public final class Node implements INode, Serializable {
public String getElementName () {
// if subnodes are also mounted as properties, try to get the "nice" prop value
// instead of the id by turning the anonymous flag off.
// Work around this for user objects to alsways return a URL like /users/username
if ("user".equalsIgnoreCase (prototype)) {
anonymous = false;
} else if (parentHandle != null) {
if (parentHandle != null && lastNameCheck < Math.max (dbmap.getLastTypeChange(), lastmodified)) {
try {
Node p = parentHandle.getNode (nmgr);
DbMapping parentmap = p.getDbMapping ();
@ -508,6 +499,7 @@ public final class Node implements INode, Serializable {
} catch (Exception ignore) {
// just fall back to default method
}
lastNameCheck = System.currentTimeMillis ();
}
return anonymous || name == null || name.length() == 0 ? id : name;
}
@ -615,10 +607,13 @@ public final class Node implements INode, Serializable {
* Register a node as parent of the present node. We can't refer to the node directly, so we use
* the ID + DB map combo.
*/
public void setParent (Node parent) {
protected void setParent (Node parent) {
parentHandle = parent == null ? null : parent.getHandle ();
}
/**
* Set the parent handle which can be used to get the actual parent node.
*/
public void setParentHandle (NodeHandle parent) {
parentHandle = parent;
}
@ -679,10 +674,11 @@ public final class Node implements INode, Serializable {
// check what's specified in the type.properties for this node.
ParentInfo[] parentInfo = null;
if (isRelational () && (lastParentSet < dbmap.getLastTypeChange() || lastParentSet < lastmodified))
if (isRelational () && lastParentSet < Math.max (dbmap.getLastTypeChange(), lastmodified))
parentInfo = dbmap.getParentInfo ();
// check if current parent candidate matches presciption, if not, try to get it
// check if current parent candidate matches presciption,
// if not, try to get one that does.
if (parentInfo != null && state != TRANSIENT) {
for (int i=0; i<parentInfo.length; i++) {
ParentInfo pinfo = parentInfo[i];
@ -693,7 +689,7 @@ public final class Node implements INode, Serializable {
if (rel != null && rel.reftype == Relation.REFERENCE)
pn = getNode (pinfo.propname, false);
// the parent of this node is the app's root node...
if (pinfo.isroot && pn == null)
if (pn == null && pinfo.isroot)
pn = nmgr.getNode ("0", nmgr.getDbMapping ("root"));
// if we found a parent node, check if we ought to use a virtual or groupby node as parent
if (pn != null) {
@ -1092,11 +1088,6 @@ public final class Node implements INode, Serializable {
// which needs to be unset
if (dbmap != null) {
Relation srel = dbmap.getSubnodeRelation ();
/*if (srel != null && srel.reftype == Relation.BACKWARD) {
Relation backlink = srel.otherType.columnNameToRelation (srel.getRemoteField ());
if (backlink != null && id.equals (node.getString (backlink.propName, false)))
node.unset (backlink.propName);
} */
}
// check if subnodes are also accessed as properties. If so, also unset the property
@ -1349,27 +1340,33 @@ public final class Node implements INode, Serializable {
prop.convertToNodeReference (pmap);
}
// the property does not exist in our propmap - see if we can create it on the fly,
// either because it is mapped from a relational database or defined as virtual node
// the property does not exist in our propmap - see if we should create it on the fly,
// either because it is mapped to an object from relational database or defined as
// collection aka virtual node
if (prop == null && dbmap != null) {
Relation prel = dbmap.getPropertyRelation (propname);
Relation srel = dbmap.getSubnodeRelation ();
if (prel == null && srel != null && srel.groupby != null)
prel = srel;
if (prel == null)
prel = dbmap.getPropertyRelation ();
if (prel != null) {
// if what we want is a virtual node, fetch anyway.
if (state == TRANSIENT && prel.virtual) {
INode node = new Node (propname, prel.getPrototype (), nmgr);
node.setDbMapping (prel.getVirtualMapping ());
Relation propRel = dbmap.getPropertyRelation (propname);
// if no property relation is defined for this specific property name,
// use the generic property relation, if one is defined.
if (propRel == null)
propRel = dbmap.getPropertyRelation ();
// so if we have a property relation and it does in fact link to another object...
if (propRel != null && propRel.isCollection ()) {
// in some cases we just want to create and set a generic node without consulting
// the NodeManager if it exists: When we get a collection (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) {
INode node = new Node (propname, propRel.getPrototype (), nmgr);
node.setDbMapping (propRel.getVirtualMapping ());
setNode (propname, node);
prop = (Property) propMap.get (propname);
// if this is from relational database only fetch if this node is itself persistent.
} else if (state != TRANSIENT && (prel.virtual || prel.accessor != null || prel.groupby != null)) {
}
// if this is from relational database only fetch if this node
// is itself persistent.
else if (state != TRANSIENT && propRel.createPropertyOnDemand ()) {
// this may be a relational node stored by property name
try {
Node pn = nmgr.getNode (this, propname, prel);
Node pn = nmgr.getNode (this, propname, propRel);
if (pn != null) {
if (pn.parentHandle == null && !"root".equalsIgnoreCase (pn.getPrototype ())) {
pn.setParent (this);
@ -1391,11 +1388,6 @@ public final class Node implements INode, Serializable {
return prop;
}
/* public String getString (String propname, String defaultValue, boolean inherit) {
String propValue = getString (propname, inherit);
return propValue == null ? defaultValue : propValue;
} */
public String getString (String propname, boolean inherit) {
// propname = propname.toLowerCase ();
Property prop = getProperty (propname, inherit);
@ -1493,10 +1485,10 @@ public final class Node implements INode, Serializable {
// check if this node is already registered with the old name; if so, remove it.
// then set parent's property to this node for the new name value
DbMapping parentmap = parent.getDbMapping ();
Relation prel = parentmap.getPropertyRelation ();
Relation propRel = parentmap.getPropertyRelation ();
String dbcolumn = dbmap.propertyToColumnName (propname);
if (prel != null && prel.accessor != null && prel.accessor.equals (dbcolumn)) {
if (propRel != null && propRel.accessor != null && propRel.accessor.equals (dbcolumn)) {
INode n = parent.getNode (value, false);
if (n != null && n != this) {
parent.unset (value);

View file

@ -25,9 +25,6 @@ public final class NodeHandle implements INodeState, Serializable {
// the node's key
private Key key;
// cached DbMapping
private transient DbMapping dbmap;
static final long serialVersionUID = 3067763116576910931L;
/**
@ -64,7 +61,8 @@ public final class NodeHandle implements INodeState, Serializable {
}
/**
* Get the key for the node described by this handle. This may only be called on persistent Nodes.
* Get the key for the node described by this handle.
* This may only be called on persistent Nodes.
*/
public Key getKey () {
if (key == null)
@ -73,7 +71,8 @@ public final class NodeHandle implements INodeState, Serializable {
}
/**
* Get the ID for the node described by this handle. This may only be called on persistent Nodes.
* Get the ID for the node described by this handle.
* This may only be called on persistent Nodes.
*/
public String getID () {
if (key == null)

View file

@ -1067,10 +1067,11 @@ public final class NodeManager {
private Node getNodeByRelation (ITransaction txn, Node home, String kstr, Relation rel) throws Exception {
Node node = null;
if (rel.virtual) {
if (rel != null && rel.virtual) {
if (rel.needsPersistence ())
node = (Node) home.createNode (kstr);
else
node = new Node (home, kstr, safe, rel.prototype);
if (rel.prototype != null) {
node.setPrototype (rel.prototype);
node.setDbMapping (app.getDbMapping (rel.prototype));

View file

@ -57,14 +57,13 @@ public final class Relation {
String accessor; // db column used to access objects through this relation
String order;
String groupbyorder;
String groupbyOrder;
String groupby;
String prototype;
String groupbyprototype;
String groupbyPrototype;
String filter;
int maxSize = 0;
// Relation subnoderelation = null; // additional relation used to filter subnodes for virtual nodes
/**
* This constructor makes a copy of an existing relation. Not all fields are copied, just those
@ -156,8 +155,8 @@ public final class Relation {
// FIXME: needs to be synchronized?
if (virtualMapping != null) {
virtualMapping.lastTypeChange = ownType.lastTypeChange;
virtualMapping.subnodesRel = getVirtualSubnodeRelation ();
virtualMapping.propertiesRel = getVirtualPropertyRelation ();
virtualMapping.subRelation = getVirtualSubnodeRelation ();
virtualMapping.propRelation = getVirtualPropertyRelation ();
}
}
}
@ -190,12 +189,12 @@ public final class Relation {
if (groupby != null && groupby.trim().length() == 0)
groupby = null;
if (groupby != null) {
groupbyorder = props.getProperty (propName+".group.order");
if (groupbyorder != null && groupbyorder.trim().length() == 0)
groupbyorder = null;
groupbyprototype = props.getProperty (propName+".group.prototype");
if (groupbyprototype != null && groupbyprototype.trim().length() == 0)
groupbyprototype = null;
groupbyOrder = props.getProperty (propName+".group.order");
if (groupbyOrder != null && groupbyOrder.trim().length() == 0)
groupbyOrder = null;
groupbyPrototype = props.getProperty (propName+".group.prototype");
if (groupbyPrototype != null && groupbyPrototype.trim().length() == 0)
groupbyPrototype = null;
// aggressive loading and caching is not supported for groupby-nodes
aggressiveLoading = aggressiveCaching = false;
}
@ -229,6 +228,55 @@ public final class Relation {
return reftype == PRIMITIVE;
}
/**
* Returns true if this Relation describes an object reference property
*/
public boolean isReference () {
return reftype == REFERENCE;
}
/**
* Returns true if this Relation describes a collection object property
*/
public boolean isCollection () {
return reftype == COLLECTION;
}
/**
* Tell wether the property described by this relation is to be handled as private, i.e.
* a change on it should not result in any changed object/collection relations.
*/
public boolean isPrivate () {
return isPrivate;
}
/**
* Returns true if the object represented by this Relation has to be
* created dynamically by the Helma objectmodel runtime as a virtual
* node. Virtual nodes are objects which are only generated on demand
* and never stored to a persistent storage.
*/
public boolean createPropertyOnDemand () {
return virtual || accessor != null || groupby != null;
}
/**
* Returns true if the object represented by this Relation has to be
* persisted in the internal db in order to be functional. This is true if
* the subnodes contained in this collection are stored in the embedded
* database. In this case, the collection itself must also be an ordinary
* object stored in the db, since a virtual collection would lose its
* its content after restarts.
*/
public boolean needsPersistence () {
if (!virtual)
return false;
if (prototype == null)
return !otherType.isRelational ();
DbMapping sub = otherType.getSubnodeMapping ();
return sub != null && !sub.isRelational ();
}
/**
* Return the prototype to be used for object reached by this relation
*/
@ -301,8 +349,8 @@ public final class Relation {
return null;
if (virtualMapping == null) {
virtualMapping = new DbMapping (ownType.app);
virtualMapping.subnodesRel = getVirtualSubnodeRelation ();
virtualMapping.propertiesRel = getVirtualPropertyRelation ();
virtualMapping.subRelation = getVirtualSubnodeRelation ();
virtualMapping.propRelation = getVirtualPropertyRelation ();
}
return virtualMapping;
}
@ -316,8 +364,8 @@ public final class Relation {
throw new RuntimeException ("getVirtualSubnodeRelation called on non-virtual relation");
Relation vr = new Relation (this);
vr.groupby = groupby;
vr.groupbyorder = groupbyorder;
vr.groupbyprototype = groupbyprototype;
vr.groupbyOrder = groupbyOrder;
vr.groupbyPrototype = groupbyPrototype;
vr.order = order;
vr.filter = filter;
vr.maxSize = maxSize;
@ -335,8 +383,8 @@ public final class Relation {
throw new RuntimeException ("getVirtualPropertyRelation called on non-virtual relation");
Relation vr = new Relation (this);
vr.groupby = groupby;
vr.groupbyorder = groupbyorder;
vr.groupbyprototype = groupbyprototype;
vr.groupbyOrder = groupbyOrder;
vr.groupbyPrototype = groupbyPrototype;
vr.order = order;
vr.filter = filter;
vr.maxSize = maxSize;
@ -352,7 +400,7 @@ public final class Relation {
throw new RuntimeException ("getGroupbySubnodeRelation called on non-group-by relation");
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, null, groupby, true));
@ -369,7 +417,7 @@ public final class Relation {
throw new RuntimeException ("getGroupbyPropertyRelation called on non-group-by relation");
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, null, groupby, true));
@ -410,8 +458,8 @@ public final class Relation {
}
if (groupby != null) {
q.append (" GROUP BY "+groupby);
if (useOrder && groupbyorder != null)
q.append (" ORDER BY "+groupbyorder);
if (useOrder && groupbyOrder != null)
q.append (" ORDER BY "+groupbyOrder);
} else if (useOrder && order != null)
q.append (" ORDER BY "+order);
return q.toString ();
@ -437,26 +485,19 @@ public final class Relation {
*/
public String getOrder () {
if (groupby != null)
return groupbyorder;
return groupbyOrder;
else
return order;
}
/**
* Tell wether the property described by this relation is to be handled as readonly/write protected.
* Tell wether the property described by this relation is to be handled
* as readonly/write protected.
*/
public boolean isReadonly () {
return readonly;
}
/**
* Tell wether the property described by this relation is to be handled as private, i.e.
* a change on it should not result in any changed object/collection relations.
*/
public boolean isPrivate () {
return isPrivate;
}
/**
* Check if the child node fullfills the constraints defined by this relation.