* 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:
parent
a9553bb471
commit
fb1f5154e4
3 changed files with 137 additions and 44 deletions
|
@ -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) {
|
||||||
|
@ -179,25 +175,37 @@ public final class DbMapping {
|
||||||
} else if (tableName == null) {
|
} else if (tableName == null) {
|
||||||
app.logEvent("*** No table name specified for prototype " + typename);
|
app.logEvent("*** No table name specified for prototype " + typename);
|
||||||
app.logEvent("*** accessing or storing a " + typename +
|
app.logEvent("*** accessing or storing a " + typename +
|
||||||
" object will cause an error.");
|
" object will cause an error.");
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -1262,7 +1306,7 @@ public final class DbMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name of the prototype which specifies the storage location
|
* 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
|
* (dbsource + tablename) for this type, or null if it is stored in the embedded
|
||||||
* db.
|
* db.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ||
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue