moved from helma.objectmodel to helma.objectmodel.db package
This commit is contained in:
parent
f0151b149c
commit
c977631ec9
7 changed files with 1886 additions and 0 deletions
122
src/helma/objectmodel/db/DbKey.java
Normal file
122
src/helma/objectmodel/db/DbKey.java
Normal file
|
@ -0,0 +1,122 @@
|
|||
// DbKey.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* This is the internal representation of a database key. It is constructed
|
||||
* out of the database URL, the table name, the user name and the database
|
||||
* key of the node and unique within each Helma application. Currently only
|
||||
* single keys are supported.
|
||||
*/
|
||||
public final class DbKey implements Key, Serializable {
|
||||
|
||||
// the name of the prototype which defines the storage of this object.
|
||||
// this is the name of the object's prototype, or one of its ancestors.
|
||||
// If null, the object is stored in the embedded db.
|
||||
private final String storageName;
|
||||
// the id that defines this key's object within the above storage space
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* make a key for a persistent Object, describing its datasource and id.
|
||||
*/
|
||||
public DbKey (DbMapping dbmap, String id) {
|
||||
this.id = id;
|
||||
this.storageName = dbmap == null ? null : dbmap.getStorageTypeName ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean equals (Object what) {
|
||||
if (what == this)
|
||||
return true;
|
||||
try {
|
||||
DbKey k = (DbKey) what;
|
||||
return (storageName == k.storageName || storageName.equals (k.storageName)) &&
|
||||
(id == k.id || id.equals (k.id));
|
||||
} catch (Exception x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode () {
|
||||
return storageName == null ? id.hashCode () : storageName.hashCode() + id.hashCode ();
|
||||
}
|
||||
|
||||
public Key getParentKey () {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getStorageName () {
|
||||
return storageName;
|
||||
}
|
||||
|
||||
public String getID () {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return storageName == null ? "["+id+"]" : storageName+"["+id+"]";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
755
src/helma/objectmodel/db/DbMapping.java
Normal file
755
src/helma/objectmodel/db/DbMapping.java
Normal file
|
@ -0,0 +1,755 @@
|
|||
// DbMapping.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import helma.framework.core.Application;
|
||||
import helma.util.Updatable;
|
||||
import helma.util.SystemProperties;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Enumeration;
|
||||
import java.util.StringTokenizer;
|
||||
import java.sql.*;
|
||||
import com.workingdogs.village.*;
|
||||
|
||||
/**
|
||||
* A DbMapping describes how a certain type of Nodes is to mapped to a
|
||||
* relational database table. Basically it consists of a set of JavaScript property-to-
|
||||
* Database row bindings which are represented by instances of the Relation class.
|
||||
*/
|
||||
|
||||
public class DbMapping implements Updatable {
|
||||
|
||||
// DbMappings belong to an application
|
||||
Application app;
|
||||
// prototype name of this mapping
|
||||
String typename;
|
||||
|
||||
// properties from where the mapping is read
|
||||
SystemProperties props;
|
||||
|
||||
// name of data source to which this mapping writes
|
||||
DbSource source;
|
||||
// name of datasource
|
||||
String sourceName;
|
||||
// name of db table
|
||||
String table;
|
||||
|
||||
// list of properties to try for parent
|
||||
ParentInfo[] parent;
|
||||
// list of properties to try as skinmanager
|
||||
String[] skinmgr;
|
||||
|
||||
// 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
|
||||
Hashtable db2prop;
|
||||
|
||||
// db field used as primary key
|
||||
String idField;
|
||||
// db field used as object name
|
||||
String nameField;
|
||||
// db field used to identify name of prototype to use for object instantiation
|
||||
String protoField;
|
||||
|
||||
// name of parent prototype, if any
|
||||
String extendsProto;
|
||||
// dbmapping of parent prototype, if any
|
||||
DbMapping parentMapping;
|
||||
boolean inheritsMapping;
|
||||
|
||||
// db field that specifies the prototype of an object
|
||||
String prototypeField;
|
||||
|
||||
// descriptor for key generation method
|
||||
private String idgen;
|
||||
// remember last key generated for this table
|
||||
long lastID;
|
||||
|
||||
// the (village) schema of the database table
|
||||
Schema schema = null;
|
||||
// the (village) keydef of the db table
|
||||
KeyDef keydef = null;
|
||||
|
||||
// timestamp of last modification of the mapping (type.properties)
|
||||
long lastTypeChange;
|
||||
// timestamp of last modification of an object of this type
|
||||
long lastDataChange;
|
||||
|
||||
|
||||
/**
|
||||
* Create an empty DbMapping
|
||||
*/
|
||||
public DbMapping () {
|
||||
|
||||
prop2db = new Hashtable ();
|
||||
db2prop = new Hashtable ();
|
||||
|
||||
parent = null;
|
||||
// subnodes = null;
|
||||
// properties = null;
|
||||
idField = "id";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DbMapping from a type.properties property file
|
||||
*/
|
||||
public DbMapping (Application app, String typename, SystemProperties props) {
|
||||
|
||||
this.app = app;
|
||||
this.typename = typename;
|
||||
|
||||
prop2db = new Hashtable ();
|
||||
db2prop = new Hashtable ();
|
||||
|
||||
parent = null;
|
||||
// subnodes = null;
|
||||
// properties = null;
|
||||
idField = "id";
|
||||
|
||||
this.props = props;
|
||||
update ();
|
||||
|
||||
app.putDbMapping (typename, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the type manager whether we need update() to be called
|
||||
*/
|
||||
public boolean needsUpdate () {
|
||||
return props.lastModified () != lastTypeChange;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read the mapping from the Properties. Return true if the properties were changed.
|
||||
* The read is split in two, this method and the rewire method. The reason is that in order
|
||||
* for rewire to work, all other db mappings must have been initialized and registered.
|
||||
*/
|
||||
public synchronized void update () {
|
||||
|
||||
table = props.getProperty ("_tablename");
|
||||
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
|
||||
prototypeField = props.getProperty ("_prototypefield");
|
||||
// see if this prototype extends (inherits from) any other prototype
|
||||
extendsProto = props.getProperty ("_extends");
|
||||
|
||||
sourceName = props.getProperty ("_datasource");
|
||||
if (sourceName != null) {
|
||||
source = app.getDbSource (sourceName);
|
||||
if (source == null) {
|
||||
app.logEvent ("*** Data Source for prototype "+typename+" does not exist: "+sourceName);
|
||||
app.logEvent ("*** accessing or storing a "+typename+" object will cause an error.");
|
||||
}
|
||||
}
|
||||
|
||||
// id field must not be null, default is "id"
|
||||
idField = props.getProperty ("_id", "id");
|
||||
|
||||
nameField = props.getProperty ("_name");
|
||||
|
||||
protoField = props.getProperty ("_prototype");
|
||||
|
||||
String parentMapping = props.getProperty ("_parent");
|
||||
if (parentMapping != null) {
|
||||
// comma-separated list of properties to be used as parent
|
||||
StringTokenizer st = new StringTokenizer (parentMapping, ",;");
|
||||
parent = new ParentInfo[st.countTokens()];
|
||||
for (int i=0; i<parent.length; i++)
|
||||
parent[i] = new ParentInfo (st.nextToken().trim());
|
||||
} else
|
||||
parent = null;
|
||||
|
||||
String skm = props.getProperty ("_skinmanager");
|
||||
if (skm != null) {
|
||||
StringTokenizer st = new StringTokenizer (skm, ",;");
|
||||
skinmgr = new String[st.countTokens()];
|
||||
for (int i=0; i<skinmgr.length; i++)
|
||||
skinmgr[i] = st.nextToken().trim();
|
||||
} else
|
||||
skinmgr = null;
|
||||
|
||||
lastTypeChange = props.lastModified ();
|
||||
// set the cached schema & keydef to null so it's rebuilt the next time around
|
||||
schema = null;
|
||||
keydef = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the second part of the property reading process, called after the first part has been
|
||||
* completed on all other mappings in this application
|
||||
*/
|
||||
public synchronized void rewire () {
|
||||
|
||||
if (extendsProto != null) {
|
||||
parentMapping = app.getDbMapping (extendsProto);
|
||||
}
|
||||
|
||||
// if (table != null && source != null) {
|
||||
// app.logEvent ("set data source for "+typename+" to "+source);
|
||||
Hashtable p2d = new Hashtable ();
|
||||
Hashtable d2p = new Hashtable ();
|
||||
|
||||
for (Enumeration e=props.keys(); e.hasMoreElements(); ) {
|
||||
String propName = (String) e.nextElement ();
|
||||
|
||||
try {
|
||||
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);
|
||||
if (rel == null)
|
||||
rel = new Relation (dbField, propName, this, props);
|
||||
else
|
||||
rel.update (dbField, props);
|
||||
p2d.put (propName, rel);
|
||||
if (rel.columnName != null &&
|
||||
(rel.reftype == Relation.PRIMITIVE ||
|
||||
rel.reftype == Relation.REFERENCE))
|
||||
d2p.put (rel.columnName, rel);
|
||||
// app.logEvent ("Mapping "+propName+" -> "+dbField);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
app.logEvent ("Error in type.properties: "+x.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
prop2db = p2d;
|
||||
db2prop = d2p;
|
||||
|
||||
String subnodeMapping = props.getProperty ("_subnodes");
|
||||
if (subnodeMapping != null) {
|
||||
try {
|
||||
// check if subnode relation already exists. If so, reuse it
|
||||
if (subnodesRel == null)
|
||||
subnodesRel = new Relation (subnodeMapping, "_subnodes", this, props);
|
||||
else
|
||||
subnodesRel.update (subnodeMapping, props);
|
||||
// if (subnodesRel.isReference ())
|
||||
// subnodes = subnodesRel.other;
|
||||
// else
|
||||
// subnodes = (DbMapping) app.getDbMapping (subnodeMapping);
|
||||
} catch (Exception x) {
|
||||
app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ());
|
||||
// subnodesRel = null;
|
||||
}
|
||||
} else
|
||||
subnodesRel = null;
|
||||
|
||||
String propertiesMapping = props.getProperty ("_properties");
|
||||
if (propertiesMapping != null) {
|
||||
try {
|
||||
// check if property relation already exists. If so, reuse it
|
||||
if (propertiesRel == null)
|
||||
propertiesRel = new Relation (propertiesMapping, "_properties", this, props);
|
||||
else
|
||||
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 reading _properties relation for "+typename+": "+x.getMessage ());
|
||||
// propertiesRel = null;
|
||||
}
|
||||
} else
|
||||
propertiesRel = null;
|
||||
|
||||
if (groupbyMapping != null) {
|
||||
groupbyMapping.subnodesRel = subnodesRel == null ? null : subnodesRel.getGroupbySubnodeRelation ();
|
||||
groupbyMapping.propertiesRel = propertiesRel == null ? null : propertiesRel.getGroupbyPropertyRelation ();
|
||||
groupbyMapping.lastTypeChange = this.lastTypeChange;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
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+".");
|
||||
}
|
||||
return source.getConnection ();
|
||||
}
|
||||
|
||||
public DbSource getDbSource () {
|
||||
if (source == null && parentMapping != null)
|
||||
return parentMapping.getDbSource ();
|
||||
return source;
|
||||
}
|
||||
|
||||
public String getSourceID () {
|
||||
if (source == null && parentMapping != null)
|
||||
return parentMapping.getSourceID ();
|
||||
return source == null ? "" : source.url;
|
||||
}
|
||||
|
||||
public String getTableName () {
|
||||
if (source == null && parentMapping != null)
|
||||
return parentMapping.getTableName ();
|
||||
return table;
|
||||
}
|
||||
|
||||
public Application getApplication () {
|
||||
return app;
|
||||
}
|
||||
|
||||
public String getAppName () {
|
||||
return app.getName();
|
||||
}
|
||||
|
||||
public String getTypeName () {
|
||||
return typename;
|
||||
}
|
||||
|
||||
public String getExtends () {
|
||||
return extendsProto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary key column name for objects using this mapping.
|
||||
*/
|
||||
public String getIDField () {
|
||||
if (idField == null && parentMapping != null)
|
||||
return parentMapping.getIDField ();
|
||||
return idField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column used for (internal) names of objects of this type.
|
||||
*/
|
||||
public String getNameField () {
|
||||
if (nameField == null && parentMapping != null)
|
||||
return parentMapping.getNameField ();
|
||||
return nameField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column used for names of prototype.
|
||||
*/
|
||||
public String getPrototypeField () {
|
||||
if (protoField == null && parentMapping != null)
|
||||
return parentMapping.getPrototypeField ();
|
||||
return protoField;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translate a database column name to an object property name according to this mapping.
|
||||
*/
|
||||
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);
|
||||
if (rel != null && (rel.reftype == Relation.PRIMITIVE || rel.reftype == Relation.REFERENCE))
|
||||
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 (propName == null)
|
||||
return null;
|
||||
if (table == null && parentMapping != null)
|
||||
return parentMapping.propertyToColumnName (propName);
|
||||
Relation rel = (Relation) prop2db.get (propName);
|
||||
if (rel != null && (rel.reftype == Relation.PRIMITIVE || rel.reftype == Relation.REFERENCE))
|
||||
return rel.columnName;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a database column name to an object property name according to this mapping.
|
||||
*/
|
||||
public Relation columnNameToRelation (String columnName) {
|
||||
if (columnName == null)
|
||||
return null;
|
||||
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 propertyToRelation (String propName) {
|
||||
if (propName == null)
|
||||
return null;
|
||||
if (table == null && parentMapping != null)
|
||||
return parentMapping.propertyToRelation (propName);
|
||||
return (Relation) prop2db.get (propName);
|
||||
}
|
||||
|
||||
|
||||
public synchronized ParentInfo[] getParentInfo () {
|
||||
if (parent == null && parentMapping != null)
|
||||
return parentMapping.getParentInfo ();
|
||||
return parent;
|
||||
}
|
||||
|
||||
public String[] getSkinManagers () {
|
||||
return skinmgr;
|
||||
}
|
||||
|
||||
|
||||
public DbMapping getSubnodeMapping () {
|
||||
if (subnodesRel != null)
|
||||
return subnodesRel.otherType;
|
||||
if (parentMapping != null)
|
||||
return parentMapping.getSubnodeMapping ();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public DbMapping getExactPropertyMapping (String propname) {
|
||||
if (propname == null)
|
||||
return null;
|
||||
Relation rel = (Relation) prop2db.get (propname.toLowerCase());
|
||||
if (rel == null && parentMapping != null)
|
||||
return parentMapping.getExactPropertyMapping (propname);
|
||||
return rel != null ? rel.otherType : null;
|
||||
}
|
||||
|
||||
public DbMapping getPropertyMapping (String propname) {
|
||||
if (propname == null) {
|
||||
if (propertiesRel != null)
|
||||
return propertiesRel.otherType;
|
||||
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
|
||||
if (rel.virtual && rel.prototype == null)
|
||||
return null;
|
||||
else
|
||||
return rel.otherType;
|
||||
}
|
||||
|
||||
if (propertiesRel != null)
|
||||
return propertiesRel.otherType;
|
||||
if (parentMapping != null)
|
||||
return parentMapping.getPropertyMapping (propname);
|
||||
return null;
|
||||
}
|
||||
|
||||
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.groupbyprototype;
|
||||
}
|
||||
return groupbyMapping;
|
||||
}
|
||||
|
||||
/* public void setPropertyMapping (DbMapping pm) {
|
||||
properties = pm;
|
||||
} */
|
||||
|
||||
public void setSubnodeRelation (Relation rel) {
|
||||
subnodesRel = rel;
|
||||
}
|
||||
|
||||
public void setPropertyRelation (Relation rel) {
|
||||
propertiesRel = rel;
|
||||
}
|
||||
|
||||
public Relation getSubnodeRelation () {
|
||||
if (subnodesRel == null && parentMapping != null)
|
||||
return parentMapping.getSubnodeRelation ();
|
||||
return subnodesRel;
|
||||
}
|
||||
|
||||
public Relation getPropertyRelation () {
|
||||
if (propertiesRel == null && parentMapping != null)
|
||||
return parentMapping.getPropertyRelation ();
|
||||
return propertiesRel;
|
||||
}
|
||||
|
||||
public Relation getPropertyRelation (String propname) {
|
||||
if (propname == null)
|
||||
return getPropertyRelation ();
|
||||
Relation rel = (Relation) prop2db.get (propname.toLowerCase());
|
||||
if (rel == null && propertiesRel == null && parentMapping != null)
|
||||
return parentMapping.getPropertyRelation (propname);
|
||||
return rel != null ? rel : propertiesRel;
|
||||
}
|
||||
|
||||
public String getSubnodeGroupby () {
|
||||
if (subnodesRel == null && parentMapping != null)
|
||||
return parentMapping.getSubnodeGroupby ();
|
||||
return subnodesRel == null ? null : subnodesRel.groupby;
|
||||
}
|
||||
|
||||
public String getIDgen () {
|
||||
if (idgen == null && parentMapping != null)
|
||||
return parentMapping.getIDgen ();
|
||||
return idgen;
|
||||
}
|
||||
|
||||
|
||||
public WrappedNodeManager getWrappedNodeManager () {
|
||||
if (app == null)
|
||||
throw new RuntimeException ("Can't get node manager from internal db mapping");
|
||||
return app.getWrappedNodeManager ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether this data mapping maps to a relational database table. This returns true
|
||||
* if a datasource is specified, even if it is not a valid one. Otherwise, objects with invalid
|
||||
* mappings would be stored in the embedded db instead of an error being thrown, which is
|
||||
* not what we want.
|
||||
*/
|
||||
public boolean isRelational () {
|
||||
if (sourceName != null)
|
||||
return true;
|
||||
if (parentMapping != null)
|
||||
return parentMapping.isRelational ();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Village Schema object for this DbMapping.
|
||||
*/
|
||||
public synchronized Schema getSchema () throws ClassNotFoundException, SQLException, DataSetException {
|
||||
if (!isRelational ())
|
||||
throw new SQLException ("Can't get Schema for non-relational data mapping");
|
||||
if (source == null && parentMapping != null)
|
||||
return parentMapping.getSchema ();
|
||||
// Use local variable s to avoid synchronization (schema may be nulled elsewhere)
|
||||
Schema s = schema;
|
||||
if (s != null)
|
||||
return s;
|
||||
schema = new Schema ().schema (getConnection (), table, "*");
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Village Schema object for this DbMapping.
|
||||
*/
|
||||
public synchronized KeyDef getKeyDef () {
|
||||
if (!isRelational ())
|
||||
throw new RuntimeException ("Can't get KeyDef for non-relational data mapping");
|
||||
if (source == null && parentMapping != null)
|
||||
return parentMapping.getKeyDef ();
|
||||
// Use local variable s to avoid synchronization (keydef may be nulled elsewhere)
|
||||
KeyDef k = keydef;
|
||||
if (k != null)
|
||||
return k;
|
||||
keydef = new KeyDef ().addAttrib (idField);
|
||||
return keydef;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
if (app == null)
|
||||
return "[unspecified internal DbMapping]";
|
||||
else
|
||||
return ("["+app.getName()+"."+typename+"]");
|
||||
}
|
||||
|
||||
public long getLastTypeChange () {
|
||||
return lastTypeChange;
|
||||
}
|
||||
|
||||
|
||||
public long getLastDataChange () {
|
||||
return lastDataChange;
|
||||
}
|
||||
|
||||
public void notifyDataChange () {
|
||||
lastDataChange = System.currentTimeMillis ();
|
||||
if (parentMapping != null && source == null)
|
||||
parentMapping.notifyDataChange ();
|
||||
}
|
||||
|
||||
public synchronized long getNewID (long dbmax) {
|
||||
if (parentMapping != null && source == null)
|
||||
return parentMapping.getNewID (dbmax);
|
||||
lastID = Math.max (dbmax+1, lastID+1);
|
||||
return lastID;
|
||||
}
|
||||
|
||||
public Hashtable getProp2DB () {
|
||||
if (table == null && parentMapping != null)
|
||||
return parentMapping.getProp2DB ();
|
||||
return prop2db;
|
||||
}
|
||||
|
||||
public Hashtable getDB2Prop () {
|
||||
if (table == null && parentMapping != null)
|
||||
return parentMapping.getDB2Prop ();
|
||||
return db2prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the prototype which specifies the storage location
|
||||
* (dbsource + tablename) for this type, or null if it is stored in the embedded
|
||||
* db.
|
||||
*/
|
||||
public String getStorageTypeName () {
|
||||
if (table == null && parentMapping != null)
|
||||
return parentMapping.getStorageTypeName ();
|
||||
return sourceName == null ? null : typename;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
121
src/helma/objectmodel/db/DbSource.java
Normal file
121
src/helma/objectmodel/db/DbSource.java
Normal file
|
@ -0,0 +1,121 @@
|
|||
// DbSource.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Hashtable;
|
||||
import helma.util.SystemProperties;
|
||||
|
||||
/**
|
||||
* This class describes a releational data source (URL, driver, user and password).
|
||||
*/
|
||||
|
||||
public class DbSource {
|
||||
|
||||
private String name;
|
||||
private SystemProperties props;
|
||||
private static SystemProperties defaultProps = null;
|
||||
protected String url;
|
||||
private String driver;
|
||||
protected String user;
|
||||
private String password;
|
||||
|
||||
private long lastRead = 0l;
|
||||
|
||||
public DbSource (String name, SystemProperties props) throws ClassNotFoundException {
|
||||
this.name = name;
|
||||
this.props = props;
|
||||
init ();
|
||||
Server.getLogger().log ("created db source ["+name+", "+url+", "+driver+", "+user+"]");
|
||||
}
|
||||
|
||||
public Connection getConnection () throws ClassNotFoundException, SQLException {
|
||||
Transactor tx = (Transactor) Thread.currentThread ();
|
||||
Connection con = tx.getConnection (this);
|
||||
boolean fileUpdated = props.lastModified () > lastRead;
|
||||
if (!fileUpdated && defaultProps != null)
|
||||
fileUpdated = defaultProps.lastModified () > lastRead;
|
||||
if (con == null || con.isClosed () || fileUpdated) {
|
||||
init ();
|
||||
Class.forName (driver);
|
||||
con = DriverManager.getConnection (url, user, password);
|
||||
// If we wanted to use SQL transactions, we'd set autoCommit to
|
||||
// false here and make commit/rollback invocations in Transactor methods;
|
||||
Server.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;
|
||||
}
|
||||
|
||||
private void init () throws ClassNotFoundException {
|
||||
lastRead = defaultProps == null ? props.lastModified () : Math.max (props.lastModified (), defaultProps.lastModified ());
|
||||
url = props.getProperty (name+".url");
|
||||
driver = props.getProperty (name+".driver");
|
||||
Class.forName (driver);
|
||||
user = props.getProperty (name+".user");
|
||||
password = props.getProperty (name+".password");
|
||||
}
|
||||
|
||||
public String getDriverName () {
|
||||
return driver;
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static void setDefaultProps (SystemProperties props) {
|
||||
defaultProps = props;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
74
src/helma/objectmodel/db/Key.java
Normal file
74
src/helma/objectmodel/db/Key.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Key.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
|
||||
/**
|
||||
* This is the interface for the internal representation of an object key.
|
||||
*
|
||||
*/
|
||||
public interface Key {
|
||||
|
||||
|
||||
public Key getParentKey ();
|
||||
|
||||
public String getID ();
|
||||
|
||||
public String getStorageName ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
41
src/helma/objectmodel/db/ParentInfo.java
Normal file
41
src/helma/objectmodel/db/ParentInfo.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
// ParentInfo.java
|
||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
|
||||
/**
|
||||
* This class describes a parent relation between releational nodes.
|
||||
*/
|
||||
|
||||
public class ParentInfo {
|
||||
|
||||
public final String propname;
|
||||
public final String virtualname;
|
||||
public final boolean named;
|
||||
public final boolean isroot;
|
||||
|
||||
|
||||
public ParentInfo (String desc) {
|
||||
int n = desc.indexOf ("[named]");
|
||||
named = n > -1;
|
||||
String d = named ? desc.substring (0, n) : desc;
|
||||
|
||||
int dot = d.indexOf (".");
|
||||
if (dot > -1) {
|
||||
propname = d.substring (0, dot).trim();
|
||||
virtualname = d.substring (dot+1).trim();
|
||||
} else {
|
||||
propname = d.trim();
|
||||
virtualname = null;
|
||||
}
|
||||
|
||||
isroot = "root".equals (propname);
|
||||
// System.err.println ("created "+this);
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return "ParentInfo["+propname+","+virtualname+","+named+"]";
|
||||
}
|
||||
|
||||
}
|
655
src/helma/objectmodel/db/Relation.java
Normal file
655
src/helma/objectmodel/db/Relation.java
Normal file
|
@ -0,0 +1,655 @@
|
|||
// Relation.java
|
||||
// Copyright (c) Hannes Wallnöfer 1997-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import helma.objectmodel.*;
|
||||
import helma.framework.core.Application;
|
||||
import java.util.Properties;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This describes how a property of a persistent Object is stored in a
|
||||
* relational database table. This can be either a scalar property (string, date, number etc.)
|
||||
* or a reference to one or more other objects.
|
||||
*/
|
||||
public class Relation {
|
||||
|
||||
// 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 REFERENCE = 1;
|
||||
// a 1-to-many relation, a field in another table points to objects of this type
|
||||
public final static int COLLECTION = 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 ownType;
|
||||
// the DbMapping of the prototype we link to, unless this is a "primitive" (non-object) relation
|
||||
public DbMapping otherType;
|
||||
|
||||
// if this relation defines a virtual node, we need to provide a DbMapping for these virtual nodes
|
||||
DbMapping virtualMapping;
|
||||
|
||||
Relation virtualRelation;
|
||||
Relation groupRelation;
|
||||
|
||||
public String propName;
|
||||
protected String columnName;
|
||||
|
||||
public int reftype;
|
||||
|
||||
public Constraint[] constraints;
|
||||
|
||||
public boolean virtual;
|
||||
public boolean readonly;
|
||||
public boolean aggressiveLoading;
|
||||
public boolean aggressiveCaching;
|
||||
public boolean subnodesAreProperties;
|
||||
public String accessor; // db column used to access objects through this 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 for virtual nodes
|
||||
|
||||
/**
|
||||
* 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 (Relation rel) {
|
||||
this.ownType = rel.ownType;
|
||||
this.otherType = rel.otherType;
|
||||
this.propName = rel.propName;
|
||||
this.columnName = rel.columnName;
|
||||
this.reftype = rel.reftype;
|
||||
this.constraints = rel.constraints;
|
||||
this.accessor = rel.accessor;
|
||||
this.subnodesAreProperties = rel.subnodesAreProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a relation entry from a line in a properties file.
|
||||
*/
|
||||
public Relation (String desc, String propName, DbMapping ownType, Properties props) {
|
||||
this.ownType = ownType;
|
||||
this.propName = propName;
|
||||
otherType = null;
|
||||
|
||||
update (desc, props);
|
||||
}
|
||||
|
||||
public void update (String desc, Properties props) {
|
||||
|
||||
boolean mountpoint = false;
|
||||
Vector cnst = null;
|
||||
|
||||
if (desc == null || "".equals (desc.trim ())) {
|
||||
if (propName != null) {
|
||||
reftype = PRIMITIVE;
|
||||
columnName = propName;
|
||||
} else {
|
||||
reftype = INVALID;
|
||||
columnName = propName;
|
||||
}
|
||||
} else {
|
||||
desc = desc.trim ();
|
||||
String descLower = desc.toLowerCase ();
|
||||
if (descLower.startsWith ("[virtual]")) {
|
||||
desc = desc.substring (9).trim ();
|
||||
virtual = true;
|
||||
} else if (descLower.startsWith ("[collection]")) {
|
||||
desc = desc.substring (12).trim ();
|
||||
virtual = true;
|
||||
} else if (descLower.startsWith ("[mountpoint]")) {
|
||||
desc = desc.substring (12).trim ();
|
||||
virtual = true;
|
||||
mountpoint = true;
|
||||
} else {
|
||||
virtual = false;
|
||||
}
|
||||
if (descLower.startsWith ("[readonly]")) {
|
||||
desc = desc.substring (10).trim ();
|
||||
readonly = true;
|
||||
} else {
|
||||
readonly = false;
|
||||
}
|
||||
}
|
||||
|
||||
// parse the basic properties of this mapping
|
||||
parseMapping (desc, mountpoint);
|
||||
|
||||
// the following options only apply to object relations
|
||||
if (reftype != PRIMITIVE && reftype != INVALID) {
|
||||
|
||||
cnst = new Vector ();
|
||||
|
||||
Constraint c = parseConstraint (desc);
|
||||
|
||||
if (c != null)
|
||||
cnst.add (c);
|
||||
|
||||
parseOptions (cnst, props);
|
||||
|
||||
constraints = new Constraint[cnst.size()];
|
||||
cnst.copyInto (constraints);
|
||||
|
||||
// System.err.println ("PARSED RELATION "+this);
|
||||
// if (accessor != null)
|
||||
// System.err.println ("SET ACCESSOR: "+accessor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a line describing a mapping of a property field. If the mapping is a
|
||||
* object reference of a collection of objects, put any constraints in the Vector.
|
||||
*/
|
||||
protected void parseMapping (String desc, boolean mountpoint) {
|
||||
|
||||
Application app = ownType.getApplication ();
|
||||
|
||||
if (desc.indexOf ("<") > -1) {
|
||||
reftype = COLLECTION;
|
||||
int lt = desc.indexOf ("<");
|
||||
int dot = desc.indexOf (".");
|
||||
String other = dot < 0 ? desc.substring (lt+1).trim () : desc.substring (lt+1, dot).trim ();
|
||||
otherType = app.getDbMapping (other);
|
||||
if (otherType == null)
|
||||
throw new RuntimeException ("DbMapping for "+other+" not found from "+ownType.typename);
|
||||
columnName = null;
|
||||
if (mountpoint)
|
||||
prototype = other;
|
||||
} else if (desc.indexOf (">") > -1) {
|
||||
reftype = REFERENCE;
|
||||
int bt = desc.indexOf (">");
|
||||
int dot = desc.indexOf (".");
|
||||
String other = dot > -1 ? desc.substring (bt+1, dot).trim () : desc.substring (bt+1).trim ();
|
||||
otherType = app.getDbMapping (other);
|
||||
if (otherType == null)
|
||||
throw new RuntimeException ("DbMapping for "+other+" not found from "+ownType.typename);
|
||||
columnName = desc.substring (0, bt).trim ();
|
||||
if (mountpoint)
|
||||
prototype = other;
|
||||
} else if (desc.indexOf (".") > -1) {
|
||||
reftype = COLLECTION;
|
||||
int dot = desc.indexOf (".");
|
||||
String other = desc.substring (0, dot).trim ();
|
||||
otherType = app.getDbMapping (other);
|
||||
if (otherType == null)
|
||||
throw new RuntimeException ("DbMapping for "+other+" not found from "+ownType.typename);
|
||||
columnName = null;
|
||||
// set accessor
|
||||
accessor = desc.substring (dot+1).trim ();
|
||||
if (mountpoint)
|
||||
prototype = other;
|
||||
} else {
|
||||
if (virtual) {
|
||||
reftype = COLLECTION;
|
||||
otherType = app.getDbMapping (desc);
|
||||
if (otherType == null)
|
||||
throw new RuntimeException ("DbMapping for "+desc+" not found from "+ownType.typename);
|
||||
if (mountpoint)
|
||||
prototype = desc;
|
||||
} else {
|
||||
reftype = PRIMITIVE;
|
||||
columnName = desc.trim ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a line describing a mapping of a property field. If the mapping is a
|
||||
* object reference of a collection of objects, put any constraints in the Vector.
|
||||
*/
|
||||
protected Constraint parseConstraint (String desc) {
|
||||
if (desc.indexOf ("<") > -1) {
|
||||
int lt = desc.indexOf ("<");
|
||||
int dot = desc.indexOf (".");
|
||||
String remoteField = dot < 0 ? null : desc.substring (dot+1).trim ();
|
||||
String localField = lt <= 0 ? null : desc.substring (0, lt).trim ();
|
||||
return new Constraint (localField, otherType.getTableName (), remoteField, false);
|
||||
} else if (desc.indexOf (">") > -1) {
|
||||
int bt = desc.indexOf (">");
|
||||
int dot = desc.indexOf (".");
|
||||
String localField = desc.substring (0, bt).trim ();
|
||||
String remoteField = dot < 0 ? null : desc.substring (dot+1).trim ();
|
||||
return new Constraint (localField, otherType.getTableName (), remoteField, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void parseOptions (Vector cnst, Properties props) {
|
||||
String loading = props.getProperty (propName+".loadmode");
|
||||
aggressiveLoading = loading != null && "aggressive".equalsIgnoreCase (loading.trim());
|
||||
String caching = props.getProperty (propName+".cachemode");
|
||||
aggressiveCaching = caching != null && "aggressive".equalsIgnoreCase (caching.trim());
|
||||
// get order property
|
||||
order = props.getProperty (propName+".order");
|
||||
if (order != null && order.trim().length() == 0)
|
||||
order = null;
|
||||
// get additional filter property
|
||||
filter = props.getProperty (propName+".filter");
|
||||
if (filter != null && filter.trim().length() == 0)
|
||||
filter = null;
|
||||
// get group by property
|
||||
groupby = props.getProperty (propName+".groupby");
|
||||
if (groupby != null && groupby.trim().length() == 0)
|
||||
groupby = null;
|
||||
if (groupby != null) {
|
||||
groupbyorder = props.getProperty (propName+".groupby.order");
|
||||
if (groupbyorder != null && groupbyorder.trim().length() == 0)
|
||||
groupbyorder = null;
|
||||
groupbyprototype = props.getProperty (propName+".groupby.prototype");
|
||||
if (groupbyprototype != null && groupbyprototype.trim().length() == 0)
|
||||
groupbyprototype = null;
|
||||
// aggressive loading and caching is not supported for groupby-nodes
|
||||
aggressiveLoading = aggressiveCaching = false;
|
||||
}
|
||||
// check if subnode condition should be applied for property relations
|
||||
if ("_properties".equalsIgnoreCase (propName) || virtual) {
|
||||
String subnodes2props = props.getProperty (propName+".aresubnodes");
|
||||
subnodesAreProperties = "true".equalsIgnoreCase (subnodes2props);
|
||||
if (virtual) {
|
||||
String subnodefilter = props.getProperty (propName+".subnoderelation");
|
||||
if (subnodefilter != null) {
|
||||
Constraint c = parseConstraint (subnodefilter);
|
||||
if (c != null) {
|
||||
cnst.add (c);
|
||||
}
|
||||
}
|
||||
}
|
||||
// update virtual mapping, if it already exists
|
||||
if (virtualMapping != null) {
|
||||
virtualMapping.subnodesRel = getVirtualSubnodeRelation ();
|
||||
virtualMapping.propertiesRel = getVirtualPropertyRelation ();
|
||||
virtualMapping.lastTypeChange = ownType.lastTypeChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a constraint to the current list of constraints
|
||||
*/
|
||||
protected void addConstraint (Constraint c) {
|
||||
if (constraints == null) {
|
||||
constraints = new Constraint[1];
|
||||
constraints[0] = c;
|
||||
} else {
|
||||
Constraint[] nc = new Constraint[constraints.length+1];
|
||||
System.arraycopy (constraints, 0, nc, 0, constraints.length);
|
||||
nc[nc.length-1] = c;
|
||||
constraints = nc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean usesPrimaryKey () {
|
||||
if (otherType != null) {
|
||||
if (reftype == REFERENCE)
|
||||
return constraints.length == 1 && constraints[0].foreignKeyIsPrimary ();
|
||||
if (reftype == COLLECTION)
|
||||
return accessor == null || accessor.equals (otherType.getIDField ());
|
||||
}
|
||||
return false;
|
||||
/*
|
||||
if (otherType == null)
|
||||
return false;
|
||||
if (remoteField == null)
|
||||
// if remote field is null, it is assumed that it points to the primary key
|
||||
return true;
|
||||
return remoteField.equalsIgnoreCase (otherType.getIDField()); */
|
||||
}
|
||||
|
||||
public Relation getSubnodeRelation () {
|
||||
// return subnoderelation;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the local field name for updates.
|
||||
*/
|
||||
public String getDbField () {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the local column name for this relation to use in where clauses of select statements.
|
||||
* This uses the home node's id as fallback if local field is not specified.
|
||||
*/
|
||||
/* public String[] getLocalFields () {
|
||||
if (constraints == null)
|
||||
return new String[0];
|
||||
String[] retval = new String[constraints.length];
|
||||
for (int i=0; i<constraints.length; i++)
|
||||
retval[i] = constraints[i].localName;
|
||||
return retval;
|
||||
} */
|
||||
|
||||
/**
|
||||
* Get the "remote" column name for this relation. Uses the remote node's id as fallback if the remote field is not specified.
|
||||
*/
|
||||
/* public String[] getRemoteFields () {
|
||||
if (constraints == null)
|
||||
return new String[0];
|
||||
String[] retval = new String[constraints.length];
|
||||
for (int i=0; i<constraints.length; i++)
|
||||
retval[i] = constraints[i].foreignName;
|
||||
return retval;
|
||||
} */
|
||||
|
||||
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.
|
||||
*/
|
||||
Relation getVirtualSubnodeRelation () {
|
||||
if (!virtual)
|
||||
throw new RuntimeException ("getVirtualSubnodeRelation called on non-virtual relation");
|
||||
Relation vr = new Relation (this);
|
||||
vr.groupby = groupby;
|
||||
vr.groupbyorder = groupbyorder;
|
||||
vr.groupbyprototype = groupbyprototype;
|
||||
vr.order = order;
|
||||
vr.filter = filter;
|
||||
vr.constraints = constraints;
|
||||
vr.aggressiveLoading = aggressiveLoading;
|
||||
vr.aggressiveCaching = aggressiveCaching;
|
||||
return vr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Relation that defines the properties of a virtual node.
|
||||
*/
|
||||
Relation getVirtualPropertyRelation () {
|
||||
if (!virtual)
|
||||
throw new RuntimeException ("getVirtualPropertyRelation called on non-virtual relation");
|
||||
Relation vr = new Relation (this);
|
||||
vr.groupby = groupby;
|
||||
vr.groupbyorder = groupbyorder;
|
||||
vr.groupbyprototype = groupbyprototype;
|
||||
vr.order = order;
|
||||
vr.filter = filter;
|
||||
vr.constraints = constraints;
|
||||
return vr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Relation that defines the subnodes of a group-by node.
|
||||
*/
|
||||
Relation getGroupbySubnodeRelation () {
|
||||
if (groupby == null)
|
||||
throw new RuntimeException ("getGroupbySubnodeRelation called on non-group-by relation");
|
||||
Relation vr = new Relation (this);
|
||||
vr.order = order;
|
||||
vr.prototype = groupbyprototype;
|
||||
vr.filter = filter;
|
||||
vr.constraints = constraints;
|
||||
vr.addConstraint (new Constraint (null, null, groupby, true));
|
||||
return vr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Relation that defines the properties of a group-by node.
|
||||
*/
|
||||
Relation getGroupbyPropertyRelation () {
|
||||
if (groupby == null)
|
||||
throw new RuntimeException ("getGroupbyPropertyRelation called on non-group-by relation");
|
||||
Relation vr = new Relation (this);
|
||||
vr.order = order;
|
||||
vr.prototype = groupbyprototype;
|
||||
vr.filter = filter;
|
||||
vr.constraints = constraints;
|
||||
vr.addConstraint (new Constraint (null, null, groupby, true));
|
||||
return vr;
|
||||
}
|
||||
|
||||
public String buildWhere (INode home, INode nonvirtual, String kstr) {
|
||||
StringBuffer q = new StringBuffer ();
|
||||
String prefix = "";
|
||||
if (kstr != null) {
|
||||
String accessColumn = accessor == null ? otherType.getIDField () : accessor;
|
||||
q.append (accessColumn);
|
||||
q.append (" = '");
|
||||
q.append (escape (kstr));
|
||||
q.append ("'");
|
||||
prefix = " AND ";
|
||||
}
|
||||
|
||||
for (int i=0; i<constraints.length; i++) {
|
||||
q.append (prefix);
|
||||
constraints[i].addToQuery (q, home, nonvirtual);
|
||||
prefix = " AND ";
|
||||
}
|
||||
|
||||
if (filter != null) {
|
||||
q.append (prefix);
|
||||
q.append (filter);
|
||||
}
|
||||
return q.toString ();
|
||||
}
|
||||
|
||||
public String buildQuery (INode home, INode nonvirtual, String kstr) {
|
||||
StringBuffer q = new StringBuffer ();
|
||||
String prefix = " WHERE ";
|
||||
if (kstr != null) {
|
||||
q.append (prefix);
|
||||
String accessColumn = accessor == null ? otherType.getIDField () : accessor;
|
||||
q.append (accessColumn);
|
||||
q.append (" = '");
|
||||
q.append (escape (kstr));
|
||||
q.append ("'");
|
||||
prefix = " AND ";
|
||||
}
|
||||
for (int i=0; i<constraints.length; i++) {
|
||||
q.append (prefix);
|
||||
constraints[i].addToQuery (q, home, nonvirtual);
|
||||
prefix = " AND ";
|
||||
}
|
||||
|
||||
if (filter != null) {
|
||||
q.append (prefix);
|
||||
q.append (filter);
|
||||
}
|
||||
if (groupby != null)
|
||||
q.append (" GROUP BY "+groupby);
|
||||
if (order != null)
|
||||
q.append (" ORDER BY "+order);
|
||||
return q.toString ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the child node fullfills the constraints defined by this relation.
|
||||
*/
|
||||
public boolean checkConstraints (Node parent, Node child) {
|
||||
for (int i=0; i<constraints.length; i++) {
|
||||
String propname = otherType.columnNameToProperty (constraints[i].foreignName);
|
||||
if (propname != null) {
|
||||
INode home = constraints[i].isGroupby ? parent : parent.getNonVirtualParent ();
|
||||
String localName = constraints[i].localName;
|
||||
String value = null;
|
||||
if (localName == null || localName.equals (ownType.getIDField ()))
|
||||
value = home.getID ();
|
||||
else if (ownType.isRelational ())
|
||||
value = home.getString (ownType.columnNameToProperty (localName), false);
|
||||
else
|
||||
value = home.getString (localName, false);
|
||||
if (value != null && !value.equals (child.getString (propname, false))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that the child node fullfills the constraints defined by this relation by setting the
|
||||
* appropriate properties
|
||||
*/
|
||||
public void setConstraints (Node parent, Node child) {
|
||||
for (int i=0; i<constraints.length; i++) {
|
||||
String propname = otherType.columnNameToProperty (constraints[i].foreignName);
|
||||
if (propname != null) {
|
||||
INode home = constraints[i].isGroupby ? parent : parent.getNonVirtualParent ();
|
||||
String localName = constraints[i].localName;
|
||||
String value = null;
|
||||
if (localName == null || localName.equals (ownType.getIDField ()))
|
||||
value = home.getID ();
|
||||
else if (ownType.isRelational ())
|
||||
value = home.getString (ownType.columnNameToProperty (localName), false);
|
||||
else
|
||||
value = home.getString (localName, false);
|
||||
if (value != null) {
|
||||
System.err.println ("SETTING "+child+"."+propname+" TO "+value);
|
||||
child.setString (propname, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// a utility method to escape single quotes
|
||||
String escape (String str) {
|
||||
if (str == null)
|
||||
return null;
|
||||
if (str.indexOf ("'") < 0)
|
||||
return str;
|
||||
int l = str.length();
|
||||
StringBuffer sbuf = new StringBuffer (l + 10);
|
||||
for (int i=0; i<l; i++) {
|
||||
char c = str.charAt (i);
|
||||
if (c == '\'')
|
||||
sbuf.append ("\\");
|
||||
sbuf.append (c);
|
||||
}
|
||||
return sbuf.toString ();
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
String c = "";
|
||||
if (constraints != null) {
|
||||
for (int i=0; i<constraints.length; i++)
|
||||
c += constraints[i].toString ();
|
||||
}
|
||||
return "Relation["+ownType+"."+propName+">"+otherType+"]" + c;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Constraint class represents a part of the where clause in the query used to
|
||||
* establish a relation between database mapped objects.
|
||||
*/
|
||||
class Constraint {
|
||||
|
||||
String localName;
|
||||
String tableName;
|
||||
String foreignName;
|
||||
boolean isGroupby;
|
||||
|
||||
Constraint (String local, String table, String foreign, boolean groupby) {
|
||||
localName = local;
|
||||
tableName = table;
|
||||
foreignName = foreign;
|
||||
isGroupby = groupby;
|
||||
}
|
||||
|
||||
public void addToQuery (StringBuffer q, INode home, INode nonvirtual) {
|
||||
String local = null;
|
||||
INode ref = isGroupby ? home : nonvirtual;
|
||||
if (localName == null)
|
||||
local = ref.getID ();
|
||||
else {
|
||||
String homeprop = ownType.columnNameToProperty (localName);
|
||||
local = ref.getString (homeprop, false);
|
||||
}
|
||||
q.append (foreignName);
|
||||
q.append (" = '");
|
||||
q.append (escape (local));
|
||||
q.append ("'");
|
||||
}
|
||||
|
||||
public boolean foreignKeyIsPrimary () {
|
||||
return foreignName == null || foreignName.equals (otherType.getIDField ());
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return ownType+"."+localName+"="+tableName+"."+foreignName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
118
src/helma/objectmodel/db/SyntheticKey.java
Normal file
118
src/helma/objectmodel/db/SyntheticKey.java
Normal file
|
@ -0,0 +1,118 @@
|
|||
// SyntheticKey.java
|
||||
// Copyright (c) Hannes Wallnöfer 1998-2000
|
||||
|
||||
package helma.objectmodel.db;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* This is the internal key for an object that is not - or not directly - fetched from a db,
|
||||
* but derived from another object. This is useful for all kinds of object accessed via a
|
||||
* symbolic name from another object, like objects mounted via a property name column,
|
||||
* virtual nodes and groupby nodes.
|
||||
*/
|
||||
public final class SyntheticKey implements Key, Serializable {
|
||||
|
||||
private final Key parentKey;
|
||||
private final String name;
|
||||
|
||||
|
||||
/**
|
||||
* make a key for a persistent Object, describing its datasource and id.
|
||||
*/
|
||||
public SyntheticKey (Key key, String name) {
|
||||
this.parentKey = key;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals (Object what) {
|
||||
if (what == this)
|
||||
return true;
|
||||
try {
|
||||
SyntheticKey k = (SyntheticKey) what;
|
||||
return parentKey.equals (k.parentKey) &&
|
||||
(name == k.name || name.equals (k.name));
|
||||
} catch (Exception x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode () {
|
||||
return name.hashCode () + parentKey.hashCode ();
|
||||
}
|
||||
|
||||
|
||||
public Key getParentKey () {
|
||||
return parentKey;
|
||||
}
|
||||
|
||||
public String getID () {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getStorageName () {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return parentKey+"/"+name;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue