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