* Implement db mapping dependencies introduced by additionalTables switch

* Try to get extract column name from sql functions when converting db column to property
* Added Javadoc comments
This commit is contained in:
hns 2005-09-16 14:31:56 +00:00
parent a9553bb471
commit fb1f5154e4
3 changed files with 137 additions and 44 deletions

View file

@ -112,6 +112,9 @@ public final class DbMapping {
// evict objects of this type when received via replication // evict objects of this type when received via replication
private boolean evictOnReplication; private boolean evictOnReplication;
// Set of mappings that depend on us and should be forwarded last data change events
HashSet dependentMappings = new HashSet();
/** /**
* Create an empty DbMapping * Create an empty DbMapping
*/ */
@ -140,12 +143,10 @@ public final class DbMapping {
db2prop = new HashMap(); db2prop = new HashMap();
columnMap = new HashMap(); columnMap = new HashMap();
parentInfo = null; parentInfo = null;
idField = null; idField = null;
this.props = props; this.props = props;
readBasicProperties();
} }
/** /**
@ -155,17 +156,12 @@ public final class DbMapping {
return props.lastModified() != lastTypeChange; return props.lastModified() != lastTypeChange;
} }
/** /**
* Read the mapping from the Properties. Return true if the properties were changed. * Read in basic properties and register dbmapping with the
* The read is split in two, this method and the rewire method. The reason is that in order * dbsource.
* for rewire to work, all other db mappings must have been initialized and registered.
*/ */
public synchronized void update() { private void readBasicProperties() {
// read in properties
tableName = props.getProperty("_table"); tableName = props.getProperty("_table");
idgen = props.getProperty("_idgen");
dbSourceName = props.getProperty("_db"); dbSourceName = props.getProperty("_db");
if (dbSourceName != null) { if (dbSourceName != null) {
@ -183,21 +179,33 @@ public final class DbMapping {
// mark mapping as invalid by nulling the dbSource field // mark mapping as invalid by nulling the dbSource field
dbSource = null; dbSource = null;
} else {
// dbSource and tableName not null - register this instance
dbSource.registerDbMapping(this);
}
} }
} }
/**
* 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() {
// read in properties
readBasicProperties();
idgen = props.getProperty("_idgen");
// if id field is null, we assume "ID" as default. We don't set it // if id field is null, we assume "ID" as default. We don't set it
// however, so that if null we check the parent prototype first. // however, so that if null we check the parent prototype first.
idField = props.getProperty("_id"); idField = props.getProperty("_id");
nameField = props.getProperty("_name"); nameField = props.getProperty("_name");
protoField = props.getProperty("_prototype"); protoField = props.getProperty("_prototype");
evictOnReplication = "true".equals(props.getProperty("_evictOnReplication"));
String parentSpec = props.getProperty("_parent"); String parentSpec = props.getProperty("_parent");
evictOnReplication = "true".equals(props.getProperty("_evictOnReplication"));
if (parentSpec != null) { if (parentSpec != null) {
// comma-separated list of properties to be used as parent // comma-separated list of properties to be used as parent
StringTokenizer st = new StringTokenizer(parentSpec, ",;"); StringTokenizer st = new StringTokenizer(parentSpec, ",;");
@ -503,6 +511,14 @@ public final class DbMapping {
return null; return null;
} }
// SEMIHACK: If columnName is a function call, try to extract actual
// column name from it
int open = columnName.indexOf('(');
int close = columnName.indexOf(')');
if (open > -1 && close > open) {
columnName = columnName.substring(open + 1, close);
}
return _columnNameToProperty(columnName.toUpperCase()); return _columnNameToProperty(columnName.toUpperCase());
} }
@ -1163,9 +1179,9 @@ public final class DbMapping {
} }
/** /**
* Return a string representation for this DbMapping
* *
* * @return a string representation
* @return ...
*/ */
public String toString() { public String toString() {
if (typename == null) { if (typename == null) {
@ -1176,18 +1192,18 @@ public final class DbMapping {
} }
/** /**
* Get the last time something changed in the Mapping
* *
* * @return time of last mapping change
* @return ...
*/ */
public long getLastTypeChange() { public long getLastTypeChange() {
return lastTypeChange; return lastTypeChange;
} }
/** /**
* Get the last time something changed in our data
* *
* * @return time of last data change
* @return ...
*/ */
public long getLastDataChange() { public long getLastDataChange() {
// refer to parent mapping if it uses the same db/table // refer to parent mapping if it uses the same db/table
@ -1199,25 +1215,48 @@ public final class DbMapping {
} }
/** /**
* * Set the last time something changed in the data, propagating the event
* to mappings that depend on us through an additionalTables switch.
*/ */
public void setLastDataChange(long t) { public void setLastDataChange(long t) {
// propagate data change timestamp to storage-compatible parent mapping // forward data change timestamp to storage-compatible parent mapping
if (inheritsStorage()) { if (inheritsStorage()) {
parentMapping.setLastDataChange(t); parentMapping.setLastDataChange(t);
} else {
lastDataChange = t;
// propagate data change timestamp to mappings that depend on us
if (!dependentMappings.isEmpty()) {
Iterator it = dependentMappings.iterator();
while(it.hasNext()) {
DbMapping dbmap = (DbMapping) it.next();
dbmap.setIndirectDataChange(t);
}
}
}
}
/**
* Set the last time something changed in the data. This is already an indirect
* data change triggered by a mapping we depend on, so we don't propagate it to
* mappings that depend on us through an additionalTables switch.
*/
protected void setIndirectDataChange(long t) {
// forward data change timestamp to storage-compatible parent mapping
if (inheritsStorage()) {
parentMapping.setIndirectDataChange(t);
} else { } else {
lastDataChange = t; lastDataChange = t;
} }
} }
/** /**
* Helper method to generate a new ID. This is only used in the special case
* when using the select(max) method and the underlying table is still empty.
* *
* * @param dbmax the maximum value already stored in db
* @param dbmax ... * @return a new and hopefully unique id
*
* @return ...
*/ */
public synchronized long getNewID(long dbmax) { protected synchronized long getNewID(long dbmax) {
// refer to parent mapping if it uses the same db/table // refer to parent mapping if it uses the same db/table
if (inheritsStorage()) { if (inheritsStorage()) {
return parentMapping.getNewID(dbmax); return parentMapping.getNewID(dbmax);
@ -1228,9 +1267,9 @@ public final class DbMapping {
} }
/** /**
* Return an enumeration of all properties defined by this db mapping.
* *
* * @return the property enumeration
* @return ...
*/ */
public Enumeration getPropertyEnumeration() { public Enumeration getPropertyEnumeration() {
HashSet set = new HashSet(); HashSet set = new HashSet();
@ -1250,6 +1289,11 @@ public final class DbMapping {
}; };
} }
/**
* Collect a set of all properties defined by this db mapping
*
* @param basket the set to put properties into
*/
private void collectPropertyNames(HashSet basket) { private void collectPropertyNames(HashSet basket) {
// fetch propnames from parent mapping first, than add our own. // fetch propnames from parent mapping first, than add our own.
if (parentMapping != null) { if (parentMapping != null) {
@ -1282,7 +1326,7 @@ public final class DbMapping {
* *
* @return true if this mapping shares its parent mapping storage * @return true if this mapping shares its parent mapping storage
*/ */
private boolean inheritsStorage() { protected boolean inheritsStorage() {
// note: tableName and dbSourceName are nulled out in update() if they // note: tableName and dbSourceName are nulled out in update() if they
// are inherited from the parent mapping. This way we know that // are inherited from the parent mapping. This way we know that
// storage is not inherited if either of them is not null. // storage is not inherited if either of them is not null.
@ -1344,20 +1388,30 @@ public final class DbMapping {
} }
/** /**
* Get the mapping we inherit from, or null
* *
* * @return the parent DbMapping, or null
* @return ...
*/ */
public DbMapping getParentMapping() { public DbMapping getParentMapping() {
return parentMapping; return parentMapping;
} }
/** /**
* Get our ResourceProperties
* *
* * @return our properties
* @return ...
*/ */
public ResourceProperties getProperties() { public ResourceProperties getProperties() {
return props; return props;
} }
/**
* Register a DbMapping that depends on this DbMapping, so that collections of other mapping
* should be reloaded if data on this mapping is updated.
*
* @param dbmap the DbMapping that depends on us
*/
protected void addDependency(DbMapping dbmap) {
this.dependentMappings.add(dbmap);
}
} }

View file

@ -23,6 +23,7 @@ import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Properties; import java.util.Properties;
import java.util.Hashtable;
/** /**
* This class describes a releational data source (URL, driver, user and password). * This class describes a releational data source (URL, driver, user and password).
@ -36,6 +37,7 @@ public class DbSource {
private String driver; private String driver;
private boolean isOracle; private boolean isOracle;
private long lastRead = 0L; private long lastRead = 0L;
private Hashtable dbmappings = new Hashtable();
/** /**
* Creates a new DbSource object. * Creates a new DbSource object.
@ -143,38 +145,59 @@ public class DbSource {
} }
/** /**
* Return the class name of the JDBC driver
* *
* * @return the class name of the JDBC driver
* @return ...
*/ */
public String getDriverName() { public String getDriverName() {
return driver; return driver;
} }
/** /**
* Return the name of the db dource
* *
* * @return the name of the db dource
* @return ...
*/ */
public String getName() { public String getName() {
return name; return name;
} }
/** /**
* Set the default (server-wide) properties
* *
* * @param props server default db.properties
* @param props ...
*/ */
public static void setDefaultProps(ResourceProperties props) { public static void setDefaultProps(ResourceProperties props) {
defaultProps = props; defaultProps = props;
} }
/** /**
* Is this an Oracle database? * Check if this DbSource represents an Oracle database
* *
* @return true if we're using an oracle JDBC driver * @return true if we're using an oracle JDBC driver
*/ */
public boolean isOracle() { public boolean isOracle() {
return isOracle; return isOracle;
} }
/**
* Register a dbmapping by its table name.
*
* @param dbmap the DbMapping instance to register
*/
protected void registerDbMapping(DbMapping dbmap) {
if (!dbmap.inheritsStorage() && dbmap.getTableName() != null) {
dbmappings.put(dbmap.getTableName().toUpperCase(), dbmap);
}
}
/**
* Look up a DbMapping instance for the given table name.
*
* @param tablename the table name
* @return the matching DbMapping instance
*/
protected DbMapping getDbMapping(String tablename) {
return (DbMapping) dbmappings.get(tablename.toUpperCase());
}
} }

View file

@ -19,6 +19,7 @@ package helma.objectmodel.db;
import helma.framework.core.Application; import helma.framework.core.Application;
import helma.objectmodel.INode; import helma.objectmodel.INode;
import helma.objectmodel.IProperty; import helma.objectmodel.IProperty;
import helma.util.StringUtils;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
@ -297,6 +298,21 @@ public final class Relation {
additionalTables = null; additionalTables = null;
} else { } else {
String ucTables = additionalTables.toUpperCase(); String ucTables = additionalTables.toUpperCase();
// create dependencies implied by additional tables
DbSource dbsource = otherType.getDbSource();
if (dbsource != null) {
String[] tables = StringUtils.split(ucTables, ", ");
for (int i=0; i<tables.length; i++) {
// Skip some join-related keyworks we might encounter here
if ("AS".equals(tables[i]) || "ON".equals(tables[i])) {
continue;
}
DbMapping dbmap = dbsource.getDbMapping(tables[i]);
if (dbmap != null) {
dbmap.addDependency(otherType);
}
}
}
// see wether the JOIN syntax is used. look for " join " with whitespaces on both sides // see wether the JOIN syntax is used. look for " join " with whitespaces on both sides
// and for "join " at the beginning: // and for "join " at the beginning:
additionalTablesJoined = (ucTables.indexOf(" JOIN ") != -1 || additionalTablesJoined = (ucTables.indexOf(" JOIN ") != -1 ||