diff --git a/src/helma/objectmodel/DbMapping.java b/src/helma/objectmodel/DbMapping.java index 3494c329..1538b7a1 100644 --- a/src/helma/objectmodel/DbMapping.java +++ b/src/helma/objectmodel/DbMapping.java @@ -37,11 +37,14 @@ public class DbMapping implements Updatable { ParentInfo[] parent; // list of properties to try for parent - DbMapping subnodes; - DbMapping properties; + // DbMapping subnodes; + // DbMapping properties; Relation subnodesRel; Relation propertiesRel; + // if this defines a subnode mapping with groupby layer, we need a DbMapping for those groupby nodes + DbMapping groupbyMapping; + // Map of property names to Relations objects Hashtable prop2db; // Map of db columns to Relations objects @@ -88,8 +91,8 @@ public class DbMapping implements Updatable { db2prop = new Hashtable (); parent = null; - subnodes = null; - properties = null; + // subnodes = null; + // properties = null; idField = "id"; } @@ -105,8 +108,8 @@ public class DbMapping implements Updatable { db2prop = new Hashtable (); parent = null; - subnodes = null; - properties = null; + // subnodes = null; + // properties = null; idField = "id"; this.props = props; @@ -191,9 +194,16 @@ public class DbMapping implements Updatable { try { if (!propName.startsWith ("_") && propName.indexOf (".") < 0) { String dbField = props.getProperty (propName); - Relation rel = new Relation (dbField, propName, this, props); + // check if a relation for this propery already exists. If so, reuse it + Relation rel = propertyToRelation (propName); + if (rel == null) + rel = new Relation (dbField, propName, this, props); + else + rel.update (dbField, props); p2d.put (propName, rel); - if (rel.localField != null) + if (rel.localField != null && + (rel.direction == Relation.PRIMITIVE || + rel.direction == Relation.FORWARD)) d2p.put (rel.localField, rel); // app.logEvent ("Mapping "+propName+" -> "+dbField); } @@ -208,14 +218,18 @@ public class DbMapping implements Updatable { String subnodeMapping = props.getProperty ("_subnodes"); if (subnodeMapping != null) { try { - subnodesRel = new Relation (subnodeMapping, "_subnodes", this, props); - if (subnodesRel.isReference ()) - subnodes = subnodesRel.other; + // check if subnode relation already exists. If so, reuse it + if (subnodesRel == null) + subnodesRel = new Relation (subnodeMapping, "_subnodes", this, props); else - subnodes = (DbMapping) app.getDbMapping (subnodeMapping); + subnodesRel.update (subnodeMapping, props); + // if (subnodesRel.isReference ()) + // subnodes = subnodesRel.other; + // else + // subnodes = (DbMapping) app.getDbMapping (subnodeMapping); } catch (Exception x) { - app.logEvent ("Error in type.properties: "+x.getMessage ()); - subnodesRel = null; + app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ()); + // subnodesRel = null; } } else subnodesRel = null; @@ -223,22 +237,30 @@ public class DbMapping implements Updatable { String propertiesMapping = props.getProperty ("_properties"); if (propertiesMapping != null) { try { - propertiesRel = new Relation (propertiesMapping, "_properties", this, props); - if (propertiesRel.isReference ()) - properties = propertiesRel.other; + // check if property relation already exists. If so, reuse it + if (propertiesRel == null) + propertiesRel = new Relation (propertiesMapping, "_properties", this, props); else - properties = (DbMapping) app.getDbMapping (propertiesMapping); + propertiesRel.update (propertiesMapping, props); + // if (propertiesRel.isReference ()) + // properties = propertiesRel.other; + // else + // properties = (DbMapping) app.getDbMapping (propertiesMapping); // take over groupby flag from subnodes, if properties are subnodes if (propertiesRel.subnodesAreProperties && subnodesRel != null) propertiesRel.groupby = subnodesRel.groupby; } catch (Exception x) { - app.logEvent ("Error in type.properties: "+x.getMessage ()); - propertiesRel = null; + app.logEvent ("Error reading _properties relation for "+typename+": "+x.getMessage ()); + // propertiesRel = null; } } else propertiesRel = null; - // app.logEvent ("rewiring: "+this); + if (groupbyMapping != null) { + groupbyMapping.subnodesRel = subnodesRel == null ? null : subnodesRel.getGroupbySubnodeRelation (); + groupbyMapping.propertiesRel = propertiesRel == null ? null : propertiesRel.getGroupbyPropertyRelation (); + groupbyMapping.lastTypeChange = this.lastTypeChange; + } } @@ -323,21 +345,46 @@ public class DbMapping implements Updatable { /** * Translate a database column name to an object property name according to this mapping. */ - public Relation columnNameToProperty (String columnName) { + public String columnNameToProperty (String columnName) { if (table == null && parentMapping != null) return parentMapping.columnNameToProperty (columnName); + Relation rel = (Relation) db2prop.get (columnName); + if (rel != null && (rel.direction == Relation.PRIMITIVE || rel.direction == Relation.FORWARD)) + return rel.propname; + return null; + } + + /** + * Translate an object property name to a database column name according to this mapping. + */ + public String propertyToColumnName (String propName) { + if (table == null && parentMapping != null) + return parentMapping.propertyToColumnName (propName); + Relation rel = (Relation) prop2db.get (propName); + if (rel != null && (rel.direction == Relation.PRIMITIVE || rel.direction == Relation.FORWARD)) + return rel.localField; + return null; + } + + /** + * Translate a database column name to an object property name according to this mapping. + */ + public Relation columnNameToRelation (String columnName) { + if (table == null && parentMapping != null) + return parentMapping.columnNameToRelation (columnName); return (Relation) db2prop.get (columnName); } /** * Translate an object property name to a database column name according to this mapping. */ - public Relation propertyToColumnName (String propName) { + public Relation propertyToRelation (String propName) { if (table == null && parentMapping != null) - return parentMapping.propertyToColumnName (propName); + return parentMapping.propertyToRelation (propName); return (Relation) prop2db.get (propName); } + public synchronized ParentInfo[] getParentInfo () { if (parent == null && parentMapping != null) return parentMapping.getParentInfo (); @@ -346,14 +393,13 @@ public class DbMapping implements Updatable { public DbMapping getSubnodeMapping () { - if (subnodes == null && parentMapping != null) + if (subnodesRel != null) + return subnodesRel.other; + if (parentMapping != null) return parentMapping.getSubnodeMapping (); - return subnodes; + return null; } - public void setSubnodeMapping (DbMapping sm) { - subnodes = sm; - } public DbMapping getExactPropertyMapping (String propname) { if (propname == null) @@ -365,8 +411,13 @@ public class DbMapping implements Updatable { } public DbMapping getPropertyMapping (String propname) { - if (propname == null) - return properties; + if (propname == null) { + if (propertiesRel != null) + return propertiesRel.other; + if (parentMapping != null) + return parentMapping.getPropertyMapping (null); + } + Relation rel = (Relation) prop2db.get (propname.toLowerCase()); if (rel != null) { // if this is a virtual node, it doesn't have a dbmapping @@ -376,16 +427,32 @@ public class DbMapping implements Updatable { return rel.other; } - if (properties == null && parentMapping != null) + if (propertiesRel != null) + return propertiesRel.other; + if (parentMapping != null) return parentMapping.getPropertyMapping (propname); - - return properties; + return null; } - public void setPropertyMapping (DbMapping pm) { - properties = pm; + public DbMapping getGroupbyMapping () { + if (subnodesRel == null || subnodesRel.groupby == null) + return null; + if (groupbyMapping == null) { + groupbyMapping = new DbMapping (); + groupbyMapping.subnodesRel = subnodesRel.getGroupbySubnodeRelation (); + if (propertiesRel != null) + groupbyMapping.propertiesRel = propertiesRel.getGroupbyPropertyRelation (); + else + groupbyMapping.propertiesRel = subnodesRel.getGroupbyPropertyRelation (); + groupbyMapping.typename = subnodesRel.prototype; + } + return groupbyMapping; } + /* public void setPropertyMapping (DbMapping pm) { + properties = pm; + } */ + public void setSubnodeRelation (Relation rel) { subnodesRel = rel; } diff --git a/src/helma/objectmodel/DbSource.java b/src/helma/objectmodel/DbSource.java index a03c9df8..6307d80a 100644 --- a/src/helma/objectmodel/DbSource.java +++ b/src/helma/objectmodel/DbSource.java @@ -44,6 +44,15 @@ public class DbSource { // false here and make commit/rollback invocations in Transactor methods; IServer.getLogger().log ("Created new Connection to "+url); tx.registerConnection (this, con); + ////////////////////////////////////////////// + /* DatabaseMetaData meta = con.getMetaData (); + ResultSet tables = meta.getCatalogs (); + while (tables.next()) + System.err.println ("********* TABLE: "+ tables.getObject (1)); + ResultSet types = meta.getTypeInfo (); + while (types.next()) + System.err.println ("******* TYPE: "+types.getObject(1) +" - "+types.getObject(2)+" - "+types.getObject(6)); + */ } return con; } diff --git a/src/helma/objectmodel/Node.java b/src/helma/objectmodel/Node.java index d9780afc..32879ac7 100644 --- a/src/helma/objectmodel/Node.java +++ b/src/helma/objectmodel/Node.java @@ -393,9 +393,7 @@ public class Node implements INode, Serializable { // node.setState (TRANSIENT); // make a db mapping good enough that the virtual node finds its subnodes DbMapping dbm = new DbMapping (); - dbm.setSubnodeMapping (rel.other); dbm.setSubnodeRelation (rel); - dbm.setPropertyMapping (rel.other); dbm.setPropertyRelation (rel); node.setDbMapping (dbm); setNode (propname, node); diff --git a/src/helma/objectmodel/Relation.java b/src/helma/objectmodel/Relation.java index dc9b30a4..4426b679 100644 --- a/src/helma/objectmodel/Relation.java +++ b/src/helma/objectmodel/Relation.java @@ -13,15 +13,27 @@ import java.util.Properties; */ public class Relation { - // TODO: explain hop mapping types + // these constants define different type of property-to-db-mappings + + // there is an error in the description of this relation public final static int INVALID = -1; + // a mapping of a non-object, scalar type public final static int PRIMITIVE = 0; + // a 1-to-1 relation, i.e. a field in the table is a foreign key to another object public final static int FORWARD = 1; + // a 1-to-many relation, a field in another table points to objects of this type public final static int BACKWARD = 2; + // direct mapping is a very powerful feature: objects of some types can be directly accessed + // by one of their properties/db fields. public final static int DIRECT = 3; + // the DbMapping of the type we come from public DbMapping home; + // the DbMapping of the prototype we link to, unless this is a "primitive" (non-object) relation public DbMapping other; + // if this relation defines a virtual node, we need a DbMapping for these virtual nodes + DbMapping virtualMapping; + public String propname; protected String localField, remoteField; public int direction; @@ -34,31 +46,39 @@ public class Relation { public String order; public String groupbyorder; public String groupby; + public String dogroupby; public String prototype; public String groupbyprototype; public String filter; - Relation subnoderelation = null; // additional relation used to filter subnodes + Relation subnoderelation = null; // additional relation used to filter subnodes for virtual nodes /** - * This constructor is used to directly construct a Relation, as opposed to reading it from a proerty file + * This constructor makes a copy of an existing relation. Not all fields are copied, just those + * which are needed in groupby- and virtual nodes defined by this relation. */ - public Relation (DbMapping other, String localField, String remoteField, int direction, boolean subnodesAreProperties) { - this.other = other; - this.localField = localField; - this.remoteField = remoteField; - this.direction = direction; - this.subnodesAreProperties = subnodesAreProperties; + public Relation (Relation rel) { + this.home = rel.home; + this.other = rel.other; + this.localField = rel.localField; + this.remoteField = rel.remoteField; + this.direction = rel.direction; + this.subnodesAreProperties = rel.subnodesAreProperties; } /** * Reads a relation entry from a line in a properties file. */ public Relation (String desc, String propname, DbMapping home, Properties props) { - this.home = home; this.propname = propname; other = null; + + update (desc, props); + } + + public void update (String desc, Properties props) { + Application app = home.getApplication (); boolean mountpoint = false; @@ -179,6 +199,12 @@ public class Relation { subnoderelation.order = order; } } + // update virtual mapping, if it already exists + if (virtualMapping != null) { + virtualMapping.subnodesRel = getVirtualSubnodeRelation (); + virtualMapping.propertiesRel = getVirtualPropertyRelation (); + virtualMapping.lastTypeChange = home.lastTypeChange; + } } } } @@ -200,23 +226,6 @@ public class Relation { return subnoderelation; } - /** - * Gets a key string to cache a node with a specific value for this relation. If the - * Relation uses the primary key return just the key value, otherwise include info on the - * used column or even the base node to avoid collisions. - */ - /* public String getKeyID (INode home, String kval) { - // if the column is not the primary key, we add the column name to the key - if ((direction == DIRECT || direction == FORWARD) && !usesPrimaryKey ()) { - // check if the subnode relation also has to be considered - if (subnodesAreProperties) - return "["+home.getID()+"]"+remoteField+"="+kval; // HACK - else - return remoteField+"="+kval; - } else { - return kval; - } - } */ /** * Get the local column name for this relation to use in where clauses of select statements. @@ -247,18 +256,29 @@ public class Relation { return remoteField; } + public DbMapping getVirtualMapping () { + if (!virtual) + return null; + if (virtualMapping == null) { + virtualMapping = new DbMapping (); + virtualMapping.subnodesRel = getVirtualSubnodeRelation (); + virtualMapping.propertiesRel = getVirtualPropertyRelation (); + } + return virtualMapping; + } + /** * Return a Relation that defines the subnodes of a virtual node. */ - public Relation getVirtualSubnodeRelation () { + Relation getVirtualSubnodeRelation () { if (!virtual) throw new RuntimeException ("getVirtualSubnodeRelation called on non-virtual relation"); Relation vr = null; if (subnoderelation != null) - vr = subnoderelation.makeClone (); + vr = new Relation (subnoderelation); else - vr = makeClone (); + vr = new Relation (this); vr.groupby = groupby; vr.groupbyorder = groupbyorder; vr.groupbyprototype = groupbyprototype; @@ -273,10 +293,10 @@ public class Relation { /** * Return a Relation that defines the properties of a virtual node. */ - public Relation getVirtualPropertyRelation () { + Relation getVirtualPropertyRelation () { if (!virtual) throw new RuntimeException ("getVirtualPropertyRelation called on non-virtual relation"); - Relation vr = makeClone (); + Relation vr = new Relation (this); vr.groupby = groupby; vr.groupbyorder = groupbyorder; vr.groupbyprototype = groupbyprototype; @@ -289,37 +309,35 @@ public class Relation { /** * Return a Relation that defines the subnodes of a group-by node. */ - public Relation getGroupbySubnodeRelation () { + Relation getGroupbySubnodeRelation () { if (groupby == null) throw new RuntimeException ("getGroupbySubnodeRelation called on non-group-by relation"); Relation vr = null; if (subnoderelation != null) - vr = subnoderelation.makeClone (); + vr = new Relation (subnoderelation); else - vr = makeClone (); + vr = new Relation (this); vr.order = order; vr.prototype = groupbyprototype; vr.filter = filter; + vr.dogroupby = groupby; return vr; } /** * Return a Relation that defines the properties of a group-by node. */ - public Relation getGroupbyPropertyRelation () { + Relation getGroupbyPropertyRelation () { if (groupby == null) throw new RuntimeException ("getGroupbyPropertyRelation called on non-group-by relation"); - Relation vr = makeClone (); + Relation vr = new Relation (this); vr.order = order; vr.prototype = groupbyprototype; vr.filter = filter; + vr.dogroupby = groupby; return vr; } - public Relation makeClone () { - return new Relation (other, localField, remoteField, direction, subnodesAreProperties); - } - public String toString () { return "Relation["+home+">"+other+"]";