Several changes/fixes in DbMapping:

* Relation mappings that are overwritten in sub-prototypes no longer override
the parent prototype's Relation object but creates its own Relation
* Generally enable all things to be overwritten in DbMappings that inherit from
another relational DbMapping. That means it should now be possible to really
change lots of things from a parent DbMapping and a child DbMapping.
* Improved property enumeration which now merges properties defined in the
DbMapping inherit chain
* Cleaned up and improved variable naming.
This commit is contained in:
hns 2003-03-05 14:33:21 +00:00
parent 49f011ab87
commit d2aa17041f
5 changed files with 158 additions and 127 deletions

View file

@ -7,6 +7,7 @@ import helma.framework.core.Application;
import helma.util.Updatable;
import helma.util.SystemProperties;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.StringTokenizer;
@ -28,15 +29,15 @@ public final class DbMapping implements Updatable {
// properties from where the mapping is read
SystemProperties props;
// name of data source to which this mapping writes
DbSource source;
// name of data dbSource to which this mapping writes
DbSource dbSource;
// name of datasource
String sourceName;
String dbSourceName;
// name of db table
String table;
String tableName;
// list of properties to try for parent
ParentInfo[] parent;
ParentInfo[] parentInfo;
// Relations describing subnodes and properties.
Relation subRelation;
@ -101,7 +102,7 @@ public final class DbMapping implements Updatable {
prop2db = new HashMap ();
db2prop = new HashMap ();
parent = null;
parentInfo = null;
idField = null;
}
@ -123,7 +124,7 @@ public final class DbMapping implements Updatable {
columnMap = new HashMap ();
parent = null;
parentInfo = null;
idField = null;
@ -145,7 +146,7 @@ public final class DbMapping implements Updatable {
*/
public synchronized void update () {
// read in properties
table = props.getProperty ("_table");
tableName = props.getProperty ("_table");
idgen = props.getProperty ("_idgen");
// see if there is a field which specifies the prototype of objects, if different prototypes
// can be stored in this table
@ -153,17 +154,17 @@ public final class DbMapping implements Updatable {
// see if this prototype extends (inherits from) any other prototype
extendsProto = props.getProperty ("_extends");
sourceName = props.getProperty ("_db");
if (sourceName != null) {
source = app.getDbSource (sourceName);
if (source == null) {
app.logEvent ("*** Data Source for prototype "+typename+" does not exist: "+sourceName);
dbSourceName = props.getProperty ("_db");
if (dbSourceName != null) {
dbSource = app.getDbSource (dbSourceName);
if (dbSource == null) {
app.logEvent ("*** Data Source for prototype "+typename+" does not exist: "+dbSourceName);
app.logEvent ("*** accessing or storing a "+typename+" object will cause an error.");
} else if (table == null) {
} else if (tableName == null) {
app.logEvent ("*** No table name specified for prototype "+typename);
app.logEvent ("*** accessing or storing a "+typename+" object will cause an error.");
// mark mapping as invalid by nulling the source field
source = null;
// mark mapping as invalid by nulling the dbSource field
dbSource = null;
}
}
@ -179,11 +180,11 @@ public final class DbMapping implements Updatable {
if (parentSpec != null) {
// comma-separated list of properties to be used as parent
StringTokenizer st = new StringTokenizer (parentSpec, ",;");
parent = new ParentInfo[st.countTokens()];
for (int i=0; i<parent.length; i++)
parent[i] = new ParentInfo (st.nextToken().trim());
parentInfo = new ParentInfo[st.countTokens()];
for (int i=0; i<parentInfo.length; i++)
parentInfo[i] = new ParentInfo (st.nextToken().trim());
} else {
parent = null;
parentInfo = null;
}
lastTypeChange = props.lastModified ();
@ -197,8 +198,8 @@ public final class DbMapping implements Updatable {
parentMapping = app.getDbMapping (extendsProto);
}
// if (table != null && source != null) {
// app.logEvent ("set data source for "+typename+" to "+source);
// if (tableName != null && dbSource != null) {
// app.logEvent ("set data dbSource for "+typename+" to "+dbSource);
HashMap p2d = new HashMap ();
HashMap d2p = new HashMap ();
@ -210,10 +211,13 @@ public final class DbMapping implements Updatable {
if (!propName.startsWith ("_") && propName.indexOf (".") < 0) {
String dbField = props.getProperty (propName);
// check if a relation for this propery already exists. If so, reuse it
Relation rel = propertyToRelation (propName);
Relation rel = (Relation) prop2db.get (propName.toLowerCase());
if (rel == null)
rel = new Relation (dbField, propName, this, props);
rel.update (dbField, props);
// key enumerations from SystemProperties are all lower case, which is why
// even though we don't do a toLowerCase() here,
// we have to when we lookup things in p2d later.
p2d.put (propName, rel);
if (rel.columnName != null &&
(rel.reftype == Relation.PRIMITIVE ||
@ -238,7 +242,7 @@ public final class DbMapping implements Updatable {
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)
if (subRelation.accessName != null || subRelation.groupby != null)
propRelation = subRelation;
} catch (Exception x) {
app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ());
@ -266,18 +270,23 @@ public final class DbMapping implements Updatable {
* Get a JDBC connection for this DbMapping.
*/
public Connection getConnection () throws ClassNotFoundException, SQLException {
// if source was previously not available, check again
if (source == null && sourceName != null)
source = app.getDbSource (sourceName);
if (sourceName == null && parentMapping != null)
if (dbSourceName == null) {
if (parentMapping != null)
return parentMapping.getConnection ();
if (source == null) {
if (sourceName == null)
throw new SQLException ("Tried to get Connection from non-relational embedded data source.");
else
throw new SQLException ("Datasource is not defined: "+sourceName+".");
throw new SQLException ("Tried to get Connection from non-relational embedded data source.");
}
return source.getConnection ();
if (tableName == null) {
throw new SQLException ("Invalid DbMapping, _table not specified: "+this);
}
// if dbSource was previously not available, check again
if (dbSource == null) {
dbSource = app.getDbSource (dbSourceName);
}
if (dbSource == null) {
throw new SQLException ("Datasource is not defined: "+dbSourceName+".");
}
return dbSource.getConnection ();
}
/**
@ -285,27 +294,23 @@ public final class DbMapping implements Updatable {
* data source including URL, JDBC driver, username and password.
*/
public DbSource getDbSource () {
if (source == null && parentMapping != null)
if (dbSource == null) {
if (tableName != null && dbSourceName != null)
dbSource = app.getDbSource (dbSourceName);
else if (parentMapping != null)
return parentMapping.getDbSource ();
return source;
}
return dbSource;
}
/**
* Get the URL of the data source used for this mapping.
*/
public String getSourceID () {
if (source == null && parentMapping != null)
return parentMapping.getSourceID ();
return source == null ? "" : source.url;
}
/**
* Get the table name used for this type mapping.
*/
public String getTableName () {
if (source == null && parentMapping != null)
if (tableName == null && parentMapping != null)
return parentMapping.getTableName ();
return table;
return tableName;
}
/**
@ -370,9 +375,13 @@ public final class DbMapping implements Updatable {
public String columnNameToProperty (String columnName) {
if (columnName == null)
return null;
if (table == null && parentMapping != null)
return parentMapping.columnNameToProperty (columnName);
Relation rel = (Relation) db2prop.get (columnName.toUpperCase ());
return _columnNameToProperty (columnName.toUpperCase());
}
private String _columnNameToProperty (final String columnName) {
Relation rel = (Relation) db2prop.get (columnName);
if (rel == null && parentMapping != null)
return parentMapping._columnNameToProperty (columnName);
if (rel != null && (rel.reftype == Relation.PRIMITIVE || rel.reftype == Relation.REFERENCE))
return rel.propName;
return null;
@ -384,11 +393,15 @@ public final class DbMapping implements Updatable {
public String propertyToColumnName (String propName) {
if (propName == null)
return null;
if (table == null && parentMapping != null)
return parentMapping.propertyToColumnName (propName);
// FIXME: prop2db stores keys in lower case, because it gets them
// from a SystemProperties object which converts keys to lower case.
Relation rel = (Relation) prop2db.get (propName.toLowerCase());
return _propertyToColumnName (propName.toLowerCase ());
}
private String _propertyToColumnName (final String propName) {
Relation rel = (Relation) prop2db.get (propName);
if (rel == null && parentMapping != null)
return parentMapping._propertyToColumnName (propName);
if (rel != null && (rel.reftype == Relation.PRIMITIVE || rel.reftype == Relation.REFERENCE))
return rel.columnName;
return null;
@ -400,9 +413,14 @@ public final class DbMapping implements Updatable {
public Relation columnNameToRelation (String columnName) {
if (columnName == null)
return null;
if (table == null && parentMapping != null)
return parentMapping.columnNameToRelation (columnName);
return (Relation) db2prop.get (columnName.toUpperCase ());
return _columnNameToRelation (columnName.toUpperCase());
}
private Relation _columnNameToRelation (final String columnName) {
Relation rel = (Relation) db2prop.get (columnName);
if (rel == null && parentMapping != null)
return parentMapping._columnNameToRelation (columnName);
return rel;
}
/**
@ -411,11 +429,16 @@ public final class DbMapping implements Updatable {
public Relation propertyToRelation (String propName) {
if (propName == null)
return null;
if (table == null && parentMapping != null)
return parentMapping.propertyToRelation (propName);
// FIXME: prop2db stores keys in lower case, because it gets them
// from a SystemProperties object which converts keys to lower case.
return (Relation) prop2db.get (propName.toLowerCase());
return _propertyToRelation (propName.toLowerCase());
}
private Relation _propertyToRelation (String propName) {
Relation rel = (Relation) prop2db.get (propName);
if (rel == null && parentMapping != null)
return parentMapping._propertyToRelation (propName);
return rel;
}
@ -424,9 +447,9 @@ public final class DbMapping implements Updatable {
* determine its parent object.
*/
public synchronized ParentInfo[] getParentInfo () {
if (parent == null && parentMapping != null)
if (parentInfo == null && parentMapping != null)
return parentMapping.getParentInfo ();
return parent;
return parentInfo;
}
@ -551,7 +574,7 @@ public final class DbMapping implements Updatable {
* not what we want.
*/
public boolean isRelational () {
if (sourceName != null)
if (dbSourceName != null)
return true;
if (parentMapping != null)
return parentMapping.isRelational ();
@ -565,7 +588,7 @@ public final class DbMapping implements Updatable {
public synchronized DbColumn[] getColumns() throws ClassNotFoundException, SQLException {
if (!isRelational ())
throw new SQLException ("Can't get columns for non-relational data mapping "+this);
if (source == null && parentMapping != null)
if (dbSource == null && parentMapping != null)
return parentMapping.getColumns ();
// Use local variable cols to avoid synchronization (schema may be nulled elsewhere)
if (columns == null) {
@ -656,7 +679,7 @@ public final class DbMapping implements Updatable {
* to be quoted in SQL queries.
*/
public boolean needsQuotes (String columnName) throws SQLException {
if (table == null && parentMapping != null)
if (tableName == null && parentMapping != null)
return parentMapping.needsQuotes (columnName);
try {
DbColumn col = getColumn (columnName);
@ -698,27 +721,38 @@ public final class DbMapping implements Updatable {
public void notifyDataChange () {
lastDataChange = System.currentTimeMillis ();
if (parentMapping != null && source == null)
if (parentMapping != null && dbSource == null)
parentMapping.notifyDataChange ();
}
public synchronized long getNewID (long dbmax) {
if (parentMapping != null && source == null)
if (parentMapping != null && dbSource == null)
return parentMapping.getNewID (dbmax);
lastID = Math.max (dbmax+1, lastID+1);
return lastID;
}
public HashMap getProp2DB () {
if (table == null && parentMapping != null)
return parentMapping.getProp2DB ();
return prop2db;
public Enumeration getPropertyEnumeration () {
HashSet set = new HashSet ();
collectPropertyNames (set);
final Iterator it = set.iterator();
return new Enumeration () {
public boolean hasMoreElements() {
return it.hasNext();
}
public Object nextElement() {
return it.next();
}
};
}
public Iterator getDBPropertyIterator () {
if (table == null && parentMapping != null)
return parentMapping.getDBPropertyIterator ();
return db2prop.values ().iterator ();
private void collectPropertyNames (HashSet basket) {
// fetch propnames from parent mapping first, than add our own.
if (parentMapping != null)
parentMapping.collectPropertyNames (basket);
if (!prop2db.isEmpty())
basket.addAll (prop2db.keySet());
}
/**
@ -727,9 +761,9 @@ public final class DbMapping implements Updatable {
* db.
*/
public String getStorageTypeName () {
if (table == null && parentMapping != null)
if (tableName == null && parentMapping != null)
return parentMapping.getStorageTypeName ();
return sourceName == null ? null : typename;
return dbSourceName == null ? null : typename;
}
/**

View file

@ -502,8 +502,8 @@ public final class Node implements INode, Serializable {
Node p = parentHandle.getNode (nmgr);
DbMapping parentmap = p.getDbMapping ();
Relation prel = parentmap.getPropertyRelation();
if (prel != null && prel.subnodesAreProperties && prel.accessor != null) {
String propname = dbmap.columnNameToProperty (prel.accessor);
if (prel != null && prel.hasAccessName()) {
String propname = dbmap.columnNameToProperty (prel.accessName);
String propvalue = getString (propname);
if (propvalue != null && propvalue.length() > 0) {
setName (propvalue);
@ -664,9 +664,9 @@ public final class Node implements INode, Serializable {
if (parentmap != null) {
// first try to retrieve name via generic property relation of parent
Relation prel = parentmap.getPropertyRelation ();
if (prel != null && prel.otherType == dbmap && prel.accessor != null) {
if (prel != null && prel.otherType == dbmap && prel.accessName != null) {
// reverse look up property used to access this via parent
Relation proprel = dbmap.columnNameToRelation (prel.accessor);
Relation proprel = dbmap.columnNameToRelation (prel.accessName);
if (proprel != null && proprel.propName != null)
newname = getString (proprel.propName);
}
@ -775,8 +775,9 @@ public final class Node implements INode, Serializable {
}
// if subnodes are defined via realation, make sure its constraints are enforced.
if (dbmap != null && dbmap.getSubnodeRelation () != null)
if (dbmap != null && dbmap.getSubnodeRelation () != null) {
dbmap.getSubnodeRelation ().setConstraints (this, node);
}
// if the new node is marked as TRANSIENT and this node is not, mark new node as NEW
if (state != TRANSIENT && node.state == TRANSIENT)
@ -836,10 +837,10 @@ public final class Node implements INode, Serializable {
// check if properties are subnodes (_properties.aresubnodes=true)
if (dbmap != null && node.dbmap != null) {
Relation prel = dbmap.getPropertyRelation();
if (prel != null && prel.accessor != null) {
Relation localrel = node.dbmap.columnNameToRelation (prel.accessor);
if (prel != null && 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
String propname = localrel == null ? prel.accessor : localrel.propName;
String propname = localrel == null ? prel.accessName : localrel.propName;
String prop = node.getString (propname);
if (prop != null && prop.length() > 0) {
INode old = getNode (prop);
@ -932,7 +933,7 @@ public final class Node implements INode, Serializable {
if (rel != null)
return (IPathElement) getNode (name);
rel = dbmap.getSubnodeRelation ();
if (rel != null && rel.groupby == null && rel.accessor != null) {
if (rel != null && rel.groupby == null && rel.accessName != null) {
if (rel.otherType != null && rel.otherType.isRelational ())
return (IPathElement) nmgr.getNode (this, name, rel);
else
@ -1112,10 +1113,10 @@ public final class Node implements INode, Serializable {
// check if subnodes are also accessed as properties. If so, also unset the property
if (dbmap != null && node.dbmap != null) {
Relation prel = dbmap.getPropertyRelation();
if (prel != null && prel.accessor != null) {
Relation localrel = node.dbmap.columnNameToRelation (prel.accessor);
if (prel != null && 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
String propname = localrel == null ? prel.accessor : localrel.propName;
String propname = localrel == null ? prel.accessName : localrel.propName;
String prop = node.getString (propname);
if (prop != null && getNode (prop) == node)
unset (prop);
@ -1309,16 +1310,12 @@ public final class Node implements INode, Serializable {
*/
public Enumeration properties () {
if (dbmap != null && dbmap.isRelational() && dbmap.getProp2DB ().size() > 0)
if (dbmap != null && dbmap.isRelational())
// return the properties defined in type.properties, if there are any
return new Enumeration () {
Iterator i = dbmap.getProp2DB().keySet().iterator();
public boolean hasMoreElements() {return i.hasNext();}
public Object nextElement () {return i.next();}
};
return dbmap.getPropertyEnumeration();
Relation prel = dbmap == null ? null : dbmap.getPropertyRelation ();
if (prel != null && prel.accessor != null && !prel.subnodesAreProperties
if (prel != null && prel.hasAccessName()
&& prel.otherType != null && prel.otherType.isRelational ())
// return names of objects from a relational db table
return nmgr.getPropertyNames (this, prel).elements ();
@ -1493,7 +1490,7 @@ public final class Node implements INode, Serializable {
propMap.put (p2, prop);
}
// check if this may have an effect on the node's URL when using subnodesAreProperties
// check if this may have an effect on the node's URL when using accessname
// but only do this if we already have a parent set, i.e. if we are already stored in the db
Node parent = parentHandle == null ? null : (Node) getParent ();
@ -1504,7 +1501,7 @@ public final class Node implements INode, Serializable {
Relation propRel = parentmap.getPropertyRelation ();
String dbcolumn = dbmap.propertyToColumnName (propname);
if (propRel != null && propRel.accessor != null && propRel.accessor.equals (dbcolumn)) {
if (propRel != null && propRel.accessName != null && propRel.accessName.equals (dbcolumn)) {
INode n = parent.getNode (value);
if (n != null && n != this) {
parent.unset (value);
@ -1748,7 +1745,7 @@ public final class Node implements INode, Serializable {
// UPDATE: using n.getKey() instead of manually constructing key. HW 2002/09/13
tx.visitCleanNode (n.getKey(), n);
// if the field is not the primary key of the property, also register it
if (rel != null && rel.accessor != null && state != TRANSIENT) {
if (rel != null && rel.accessName != null && state != TRANSIENT) {
Key secKey = new SyntheticKey (getKey (), propname);
nmgr.evictKey (secKey);
tx.visitCleanNode (secKey, n);

View file

@ -1027,8 +1027,8 @@ public final class NodeManager {
}
String accessProp = null;
if (rel.accessor != null && !rel.usesPrimaryKey ())
accessProp = dbm.columnNameToProperty (rel.accessor);
if (rel.accessName != null && !rel.usesPrimaryKey ())
accessProp = dbm.columnNameToProperty (rel.accessName);
while (rs.next ()) {
// create new Nodes.
@ -1048,7 +1048,7 @@ public final class NodeManager {
sn.add (new NodeHandle (primKey));
}
// if relation doesn't use primary key as accessor, get accessor value
// if relation doesn't use primary key as accessName, get accessName value
String accessName = null;
if (accessProp != null) {
accessName = node.getString (accessProp);
@ -1166,7 +1166,7 @@ public final class NodeManager {
Vector retval = new Vector ();
// if we do a groupby query (creating an intermediate layer of groupby nodes),
// retrieve the value of that field instead of the primary key
String namefield = rel.accessor;
String namefield = rel.accessName;
Connection con = rel.otherType.getConnection ();
String table = rel.otherType.getTableName ();
@ -1296,7 +1296,7 @@ public final class NodeManager {
if (home.getSubnodeRelation () != null) {
// combine our key with the constraints in the manually set subnode relation
q.append ("WHERE ");
q.append (rel.accessor);
q.append (rel.accessName);
q.append (" = '");
q.append (escape(kstr));
q.append ("'");

View file

@ -238,7 +238,7 @@ public final class Property implements IProperty, Serializable, Cloneable {
// check if the property node is also a subnode
// BUG: this doesn't work because properties for subnode/properties are never stored and therefore
// never reused.
if (nvrel != null && nvrel.subnodesAreProperties) {
if (nvrel != null && nvrel.hasAccessName()) {
node.removeNode (nvalue);
}
// only need to call unregisterPropLink if the value node is not stored in a relational db

View file

@ -52,10 +52,9 @@ public final class Relation {
boolean readonly;
boolean aggressiveLoading;
boolean aggressiveCaching;
boolean subnodesAreProperties;
boolean isPrivate;
String accessor; // db column used to access objects through this relation
String accessName; // db column used to access objects through this relation
String order;
String groupbyOrder;
String groupby;
@ -76,9 +75,8 @@ public final class Relation {
this.columnName = rel.columnName;
this.reftype = rel.reftype;
this.constraints = rel.constraints;
this.accessor = rel.accessor;
this.accessName = rel.accessName;
this.maxSize = rel.maxSize;
this.subnodesAreProperties = rel.subnodesAreProperties;
}
/**
@ -199,9 +197,7 @@ public final class Relation {
aggressiveLoading = aggressiveCaching = false;
}
// check if subnode condition should be applied for property relations
accessor = props.getProperty (propName+".accessname");
if (accessor != null)
subnodesAreProperties = true;
accessName = props.getProperty (propName+".accessname");
// parse contstraints
String local = props.getProperty (propName+".local");
String foreign = props.getProperty (propName+".foreign");
@ -257,7 +253,7 @@ public final class Relation {
* and never stored to a persistent storage.
*/
public boolean createPropertyOnDemand () {
return virtual || accessor != null || groupby != null;
return virtual || accessName != null || groupby != null;
}
/**
@ -321,13 +317,17 @@ public final class Relation {
if (reftype == REFERENCE)
return constraints.length == 1 && constraints[0].foreignKeyIsPrimary ();
if (reftype == COLLECTION)
return accessor == null || accessor.equalsIgnoreCase (otherType.getIDField ());
return accessName == null || accessName.equalsIgnoreCase (otherType.getIDField ());
}
return false;
}
public String getAccessor () {
return accessor;
public boolean hasAccessName () {
return accessName != null;
}
public String getAccessName () {
return accessName;
}
public Relation getSubnodeRelation () {
@ -445,7 +445,7 @@ public final class Relation {
String prefix = pre;
if (kstr != null) {
q.append (prefix);
String accessColumn = accessor == null ? otherType.getIDField () : accessor;
String accessColumn = accessName == null ? otherType.getIDField () : accessName;
q.append (accessColumn);
q.append (" = ");
// check if column is string type and value needs to be quoted