* From Manfred's last patch for bug 468:
- Factor out repetitive SQL query building tasks into DbMapping.appendCondition() - Implement automatic extended prototype filter for collections - add prototype ids, but with simplified implementation (be agnostic about numeric ids vs. prototype names) * Rewrite relational node insertion code * Make better use of DbColumn class wherever possible * Minor code improvements throughout the place
This commit is contained in:
parent
37f26241c4
commit
56f83cb75b
4 changed files with 297 additions and 275 deletions
|
@ -16,10 +16,11 @@
|
||||||
|
|
||||||
package helma.objectmodel.db;
|
package helma.objectmodel.db;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that encapsulates the Column name and data type of a
|
* A class that encapsulates the Column name and data type of a column in a
|
||||||
* column in a relational table.
|
* relational table.
|
||||||
*/
|
*/
|
||||||
public final class DbColumn {
|
public final class DbColumn {
|
||||||
private final String name;
|
private final String name;
|
||||||
|
@ -30,8 +31,6 @@ public final class DbColumn {
|
||||||
private final boolean isPrototype;
|
private final boolean isPrototype;
|
||||||
private final boolean isName;
|
private final boolean isName;
|
||||||
|
|
||||||
private final boolean isMapped;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -47,8 +46,6 @@ public final class DbColumn {
|
||||||
isId = name.equalsIgnoreCase(dbmap.getIDField());
|
isId = name.equalsIgnoreCase(dbmap.getIDField());
|
||||||
isPrototype = name.equalsIgnoreCase(dbmap.getPrototypeField());
|
isPrototype = name.equalsIgnoreCase(dbmap.getPrototypeField());
|
||||||
isName = name.equalsIgnoreCase(dbmap.getNameField());
|
isName = name.equalsIgnoreCase(dbmap.getNameField());
|
||||||
|
|
||||||
isMapped = relation != null || isId || isPrototype || isName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,7 +94,33 @@ public final class DbColumn {
|
||||||
* Returns true if this field is mapped by the prototype's db mapping.
|
* Returns true if this field is mapped by the prototype's db mapping.
|
||||||
*/
|
*/
|
||||||
public boolean isMapped() {
|
public boolean isMapped() {
|
||||||
return isMapped;
|
// Note: not sure if check for primitive or reference relation is really
|
||||||
|
// needed, but we did it before, so we leave it in for safety.
|
||||||
|
return isId || isPrototype || isName ||
|
||||||
|
(relation != null && (relation.isPrimitive() || relation.isReference()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether values for this column need to be quoted in insert/update
|
||||||
|
* stmts
|
||||||
|
*
|
||||||
|
* @return true if values need to be wrapped in quotes
|
||||||
|
*/
|
||||||
|
public boolean needsQuotes() {
|
||||||
|
switch (type) {
|
||||||
|
case Types.CHAR:
|
||||||
|
case Types.VARCHAR:
|
||||||
|
case Types.LONGVARCHAR:
|
||||||
|
case Types.BINARY:
|
||||||
|
case Types.VARBINARY:
|
||||||
|
case Types.LONGVARBINARY:
|
||||||
|
case Types.DATE:
|
||||||
|
case Types.TIME:
|
||||||
|
case Types.TIMESTAMP:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,13 @@ public final class DbMapping {
|
||||||
// db field used to identify name of prototype to use for object instantiation
|
// db field used to identify name of prototype to use for object instantiation
|
||||||
private String protoField;
|
private String protoField;
|
||||||
|
|
||||||
// name of parent prototype, if any
|
// Used to map prototype ids to prototype names for
|
||||||
private String extendsProto;
|
// prototypes which extend the prototype represented by
|
||||||
|
// this DbMapping.
|
||||||
|
private ResourceProperties extensionMap;
|
||||||
|
|
||||||
|
// a numeric or literal id used to represent this type in db
|
||||||
|
private String extensionId;
|
||||||
|
|
||||||
// dbmapping of parent prototype, if any
|
// dbmapping of parent prototype, if any
|
||||||
private DbMapping parentMapping;
|
private DbMapping parentMapping;
|
||||||
|
@ -221,7 +226,7 @@ public final class DbMapping {
|
||||||
lastTypeChange = props.lastModified();
|
lastTypeChange = props.lastModified();
|
||||||
|
|
||||||
// see if this prototype extends (inherits from) any other prototype
|
// see if this prototype extends (inherits from) any other prototype
|
||||||
extendsProto = props.getProperty("_extends");
|
String extendsProto = props.getProperty("_extends");
|
||||||
|
|
||||||
if (extendsProto != null) {
|
if (extendsProto != null) {
|
||||||
parentMapping = app.getDbMapping(extendsProto);
|
parentMapping = app.getDbMapping(extendsProto);
|
||||||
|
@ -245,6 +250,10 @@ public final class DbMapping {
|
||||||
parentMapping = null;
|
parentMapping = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if there is an extension-id specified inside the type.properties
|
||||||
|
extensionId = props.getProperty("_extensionId", typename);
|
||||||
|
registerExtension(extensionId, typename);
|
||||||
|
|
||||||
// set the parent prototype in the corresponding Prototype object!
|
// set the parent prototype in the corresponding Prototype object!
|
||||||
// this was previously done by TypeManager, but we need to do it
|
// this was previously done by TypeManager, but we need to do it
|
||||||
// ourself because DbMapping.update() may be called by other code than
|
// ourself because DbMapping.update() may be called by other code than
|
||||||
|
@ -361,6 +370,57 @@ public final class DbMapping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given extensionId and the coresponding prototypename
|
||||||
|
* to extensionMap for later lookup.
|
||||||
|
* @param extID the id mapping to the prototypename recogniced by helma
|
||||||
|
* @param extName the name of the extending prototype
|
||||||
|
*/
|
||||||
|
private void registerExtension(String extID, String extName) {
|
||||||
|
// lazy initialization of extensionMap
|
||||||
|
if (extensionMap == null) {
|
||||||
|
extensionMap = new ResourceProperties();
|
||||||
|
extensionMap.setIgnoreCase(true);
|
||||||
|
} else if (extensionMap.containsValue(extName)) {
|
||||||
|
// remove any preexisting mapping for the given childmapping
|
||||||
|
extensionMap.values().remove(extName);
|
||||||
|
}
|
||||||
|
extensionMap.setProperty(extID, extName);
|
||||||
|
if (inheritsStorage()) {
|
||||||
|
parentMapping.registerExtension(extID, extName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Set of Prototypes extending this prototype
|
||||||
|
* @return the Set of Prototypes extending this prototype
|
||||||
|
*/
|
||||||
|
public String[] getExtensions() {
|
||||||
|
return extensionMap == null
|
||||||
|
? new String[] { extensionId }
|
||||||
|
: (String[]) extensionMap.keySet().toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the prototype-name identified by the given integer value
|
||||||
|
* @param id the id specified for the prototype
|
||||||
|
* @return the name of the extending prototype
|
||||||
|
*/
|
||||||
|
public String getPrototypeName(String id) {
|
||||||
|
if (inheritsStorage()) {
|
||||||
|
return parentMapping.getPrototypeName(id);
|
||||||
|
}
|
||||||
|
// fallback to base-prototype if the proto isn't recogniced
|
||||||
|
return extensionMap.getProperty(id, typename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the id-value of this extension
|
||||||
|
*/
|
||||||
|
public String getExtensionId() {
|
||||||
|
return extensionId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method in interface Updatable.
|
* Method in interface Updatable.
|
||||||
*/
|
*/
|
||||||
|
@ -459,7 +519,7 @@ public final class DbMapping {
|
||||||
* Get the name of this type's parent type, if any.
|
* Get the name of this type's parent type, if any.
|
||||||
*/
|
*/
|
||||||
public String getExtends() {
|
public String getExtends() {
|
||||||
return extendsProto;
|
return parentMapping == null ? null : parentMapping.getTypeName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -937,27 +997,20 @@ public final class DbMapping {
|
||||||
*/
|
*/
|
||||||
public DbColumn getColumn(String columnName)
|
public DbColumn getColumn(String columnName)
|
||||||
throws ClassNotFoundException, SQLException {
|
throws ClassNotFoundException, SQLException {
|
||||||
|
|
||||||
DbColumn col = (DbColumn) columnMap.get(columnName);
|
DbColumn col = (DbColumn) columnMap.get(columnName);
|
||||||
|
|
||||||
if (col == null) {
|
if (col == null) {
|
||||||
DbColumn[] cols = columns;
|
DbColumn[] cols = columns;
|
||||||
|
|
||||||
if (cols == null) {
|
if (cols == null) {
|
||||||
cols = getColumns();
|
cols = getColumns();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < cols.length; i++) {
|
for (int i = 0; i < cols.length; i++) {
|
||||||
if (columnName.equalsIgnoreCase(cols[i].getName())) {
|
if (columnName.equalsIgnoreCase(cols[i].getName())) {
|
||||||
col = cols[i];
|
col = cols[i];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
columnMap.put(columnName, col);
|
columnMap.put(columnName, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
return col;
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1057,24 +1110,22 @@ public final class DbMapping {
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuffer b1 = new StringBuffer("INSERT INTO ");
|
StringBuffer b1 = new StringBuffer("INSERT INTO ");
|
||||||
|
StringBuffer b2 = new StringBuffer(" ) VALUES ( ");
|
||||||
b1.append(getTableName());
|
b1.append(getTableName());
|
||||||
b1.append(" ( ");
|
b1.append(" ( ");
|
||||||
b1.append(getIDField());
|
|
||||||
|
|
||||||
StringBuffer b2 = new StringBuffer(" ) VALUES ( ?");
|
|
||||||
|
|
||||||
DbColumn[] cols = getColumns();
|
DbColumn[] cols = getColumns();
|
||||||
|
boolean needsComma = false;
|
||||||
|
|
||||||
for (int i = 0; i < cols.length; i++) {
|
for (int i = 0; i < cols.length; i++) {
|
||||||
Relation rel = cols[i].getRelation();
|
if (cols[i].isMapped()) {
|
||||||
String name = cols[i].getName();
|
if (needsComma) {
|
||||||
|
b1.append(", ");
|
||||||
if (((rel != null) && (rel.isPrimitive() ||
|
b2.append(", ");
|
||||||
rel.isReference())) ||
|
}
|
||||||
name.equalsIgnoreCase(getNameField()) ||
|
b1.append(cols[i].getName());
|
||||||
name.equalsIgnoreCase(getPrototypeField())) {
|
b2.append("?");
|
||||||
b1.append(", ").append(cols[i].getName());
|
needsComma = true;
|
||||||
b2.append(", ?");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1115,36 +1166,16 @@ public final class DbMapping {
|
||||||
* Return true if values for the column identified by the parameter need
|
* Return true if values for the column identified by the parameter need
|
||||||
* to be quoted in SQL queries.
|
* to be quoted in SQL queries.
|
||||||
*/
|
*/
|
||||||
public boolean needsQuotes(String columnName) throws SQLException {
|
public boolean needsQuotes(String columnName) throws SQLException, ClassNotFoundException {
|
||||||
if ((tableName == null) && (parentMapping != null)) {
|
if ((tableName == null) && (parentMapping != null)) {
|
||||||
return parentMapping.needsQuotes(columnName);
|
return parentMapping.needsQuotes(columnName);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
DbColumn col = getColumn(columnName);
|
DbColumn col = getColumn(columnName);
|
||||||
|
|
||||||
// This is not a mapped column. In case of doubt, add quotes.
|
// This is not a mapped column. In case of doubt, add quotes.
|
||||||
if (col == null) {
|
if (col == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
|
return col.needsQuotes();
|
||||||
switch (col.getType()) {
|
|
||||||
case Types.CHAR:
|
|
||||||
case Types.VARCHAR:
|
|
||||||
case Types.LONGVARCHAR:
|
|
||||||
case Types.BINARY:
|
|
||||||
case Types.VARBINARY:
|
|
||||||
case Types.LONGVARBINARY:
|
|
||||||
case Types.DATE:
|
|
||||||
case Types.TIME:
|
|
||||||
case Types.TIMESTAMP:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (Exception x) {
|
|
||||||
throw new SQLException(x.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1367,10 +1398,7 @@ public final class DbMapping {
|
||||||
// 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.
|
||||||
if (parentMapping == null || tableName != null || dbSourceName != null) {
|
return parentMapping != null && tableName == null && dbSourceName == null;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1451,4 +1479,90 @@ public final class DbMapping {
|
||||||
protected void addDependency(DbMapping dbmap) {
|
protected void addDependency(DbMapping dbmap) {
|
||||||
this.dependentMappings.add(dbmap);
|
this.dependentMappings.add(dbmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a sql-condition for the given column which must have
|
||||||
|
* one of the values contained inside the given Set to the given
|
||||||
|
* StringBuffer.
|
||||||
|
* @param q the StringBuffer to append to
|
||||||
|
* @param column the column which must match one of the values
|
||||||
|
* @param values the list of values
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
protected void appendCondition(StringBuffer q, String column, String[] values)
|
||||||
|
throws SQLException, ClassNotFoundException {
|
||||||
|
if (values.length == 1) {
|
||||||
|
appendCondition(q, column, values[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (column.indexOf('(') == -1 && column.indexOf('.') == -1) {
|
||||||
|
q.append(getTableName()).append(".");
|
||||||
|
}
|
||||||
|
q.append(column).append(" in (");
|
||||||
|
|
||||||
|
if (needsQuotes(column)) {
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
q.append(", ");
|
||||||
|
q.append("'").append(escape(values[i])).append("'");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
q.append(", ");
|
||||||
|
q.append(values[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a sql-condition for the given column which must have
|
||||||
|
* the value given to the given StringBuffer.
|
||||||
|
* @param q the StringBuffer to append to
|
||||||
|
* @param column the column which must match one of the values
|
||||||
|
* @param val the value
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
protected void appendCondition(StringBuffer q, String column, String val)
|
||||||
|
throws SQLException, ClassNotFoundException {
|
||||||
|
if (column.indexOf('(') == -1 && column.indexOf('.') == -1) {
|
||||||
|
q.append(getTableName()).append(".");
|
||||||
|
}
|
||||||
|
q.append(column).append(" = ");
|
||||||
|
|
||||||
|
if (needsQuotes(column)) {
|
||||||
|
q.append("'").append(escape(val)).append("'");
|
||||||
|
} else {
|
||||||
|
q.append(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a utility method to escape single quotes used for inserting
|
||||||
|
* string-values into relational databases.
|
||||||
|
* Searches for "'" characters and escapes them by duplicating them (= "''")
|
||||||
|
* @param str the string to escape
|
||||||
|
* @return the escaped string
|
||||||
|
*/
|
||||||
|
static String escape(String str) {
|
||||||
|
if (str == null) {
|
||||||
|
return null;
|
||||||
|
} else 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();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -478,47 +478,33 @@ public final class NodeManager {
|
||||||
// app.logEvent ("inserting relational node: " + node.getID ());
|
// app.logEvent ("inserting relational node: " + node.getID ());
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
|
|
||||||
String nameField = dbm.getNameField();
|
|
||||||
String prototypeField = dbm.getPrototypeField();
|
|
||||||
|
|
||||||
long logTimeStart = logSql ? System.currentTimeMillis() : 0;
|
long logTimeStart = logSql ? System.currentTimeMillis() : 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int stmtNumber = 1;
|
int stmtNumber = 1;
|
||||||
|
|
||||||
// first column of insert statement is always the primary key
|
|
||||||
stmt.setString(stmtNumber, node.getID());
|
|
||||||
|
|
||||||
Hashtable propMap = node.getPropMap();
|
|
||||||
|
|
||||||
for (int i = 0; i < columns.length; i++) {
|
for (int i = 0; i < columns.length; i++) {
|
||||||
Relation rel = columns[i].getRelation();
|
DbColumn col = columns[i];
|
||||||
Property p = null;
|
if (!col.isMapped())
|
||||||
|
|
||||||
if (rel != null && propMap != null && (rel.isPrimitive() || rel.isReference())) {
|
|
||||||
p = (Property) propMap.get(rel.getPropName());
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = columns[i].getName();
|
|
||||||
|
|
||||||
if (!((rel != null) && (rel.isPrimitive() || rel.isReference())) &&
|
|
||||||
!name.equalsIgnoreCase(nameField) &&
|
|
||||||
!name.equalsIgnoreCase(prototypeField)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
if (col.isIdField()) {
|
||||||
|
setStatementValue(stmt, stmtNumber, node.getID(), col);
|
||||||
stmtNumber++;
|
} else if (col.isPrototypeField()) {
|
||||||
if (p!=null) {
|
setStatementValue(stmt, stmtNumber, dbm.getExtensionId(), col);
|
||||||
this.setStatementValues (stmt, stmtNumber, p, columns[i].getType());
|
|
||||||
} else if (name.equalsIgnoreCase(nameField)) {
|
|
||||||
stmt.setString(stmtNumber, node.getName());
|
|
||||||
} else if (name.equalsIgnoreCase(prototypeField)) {
|
|
||||||
stmt.setString(stmtNumber, node.getPrototype());
|
|
||||||
} else {
|
} else {
|
||||||
stmt.setNull(stmtNumber, columns[i].getType());
|
Relation rel = col.getRelation();
|
||||||
}
|
Property p = rel == null ? null : node.getProperty(rel.getPropName());
|
||||||
}
|
|
||||||
|
|
||||||
|
if (p != null) {
|
||||||
|
setStatementValue(stmt, stmtNumber, p, col.getType());
|
||||||
|
} else if (col.isNameField()) {
|
||||||
|
stmt.setString(stmtNumber, node.getName());
|
||||||
|
} else {
|
||||||
|
stmt.setNull(stmtNumber, col.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stmtNumber += 1;
|
||||||
|
}
|
||||||
stmt.executeUpdate();
|
stmt.executeUpdate();
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -533,7 +519,6 @@ public final class NodeManager {
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -607,16 +592,7 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
b.append(" WHERE ");
|
b.append(" WHERE ");
|
||||||
b.append(dbm.getIDField());
|
dbm.appendCondition(b, dbm.getIDField(), node.getID());
|
||||||
b.append(" = ");
|
|
||||||
|
|
||||||
if (dbm.needsQuotes(dbm.getIDField())) {
|
|
||||||
b.append("'");
|
|
||||||
b.append(escape(node.getID()));
|
|
||||||
b.append("'");
|
|
||||||
} else {
|
|
||||||
b.append(node.getID());
|
|
||||||
}
|
|
||||||
|
|
||||||
Connection con = dbm.getConnection();
|
Connection con = dbm.getConnection();
|
||||||
// set connection to write mode
|
// set connection to write mode
|
||||||
|
@ -637,7 +613,7 @@ public final class NodeManager {
|
||||||
Relation rel = dbm.propertyToRelation(p.getName());
|
Relation rel = dbm.propertyToRelation(p.getName());
|
||||||
|
|
||||||
stmtNumber++;
|
stmtNumber++;
|
||||||
this.setStatementValues (stmt, stmtNumber, p, rel.getColumnType());
|
setStatementValue(stmt, stmtNumber, p, rel.getColumnType());
|
||||||
|
|
||||||
p.dirty = false;
|
p.dirty = false;
|
||||||
|
|
||||||
|
@ -1233,10 +1209,8 @@ public final class NodeManager {
|
||||||
throws Exception {
|
throws Exception {
|
||||||
DbMapping dbm = rel.otherType;
|
DbMapping dbm = rel.otherType;
|
||||||
|
|
||||||
if ((dbm == null) || !dbm.isRelational()) {
|
|
||||||
// this does nothing for objects in the embedded database
|
// this does nothing for objects in the embedded database
|
||||||
return;
|
if (dbm != null && dbm.isRelational()) {
|
||||||
} else {
|
|
||||||
int missing = cache.containsKeys(keys);
|
int missing = cache.containsKeys(keys);
|
||||||
|
|
||||||
if (missing > 0) {
|
if (missing > 0) {
|
||||||
|
@ -1251,39 +1225,17 @@ public final class NodeManager {
|
||||||
long logTimeStart = logSql ? System.currentTimeMillis() : 0;
|
long logTimeStart = logSql ? System.currentTimeMillis() : 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
StringBuffer b = dbm.getSelect(null);
|
StringBuffer b = dbm.getSelect(null).append(" WHERE ");
|
||||||
|
|
||||||
String idfield = (rel.groupby != null) ? rel.groupby : dbm.getIDField();
|
String idfield = (rel.groupby != null) ? rel.groupby : dbm.getIDField();
|
||||||
boolean needsQuotes = dbm.needsQuotes(idfield);
|
|
||||||
|
|
||||||
b.append(" WHERE ");
|
String[] ids = new String[missing];
|
||||||
b.append(dbm.getTableName());
|
int j = 0;
|
||||||
b.append(".");
|
for (int k = 0; k < keys.length; k++) {
|
||||||
b.append(idfield);
|
if (keys[k] != null)
|
||||||
b.append(" IN (");
|
ids[j++] = keys[k].getID();
|
||||||
|
|
||||||
boolean first = true;
|
|
||||||
|
|
||||||
for (int i = 0; i < keys.length; i++) {
|
|
||||||
if (keys[i] != null) {
|
|
||||||
if (!first) {
|
|
||||||
b.append(',');
|
|
||||||
} else {
|
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsQuotes) {
|
dbm.appendCondition(b, idfield, ids);
|
||||||
b.append("'");
|
|
||||||
b.append(escape(keys[i].getID()));
|
|
||||||
b.append("'");
|
|
||||||
} else {
|
|
||||||
b.append(keys[i].getID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.append(") ");
|
|
||||||
|
|
||||||
dbm.addJoinConstraints(b, " AND ");
|
dbm.addJoinConstraints(b, " AND ");
|
||||||
|
|
||||||
if (rel.groupby != null) {
|
if (rel.groupby != null) {
|
||||||
|
@ -1388,7 +1340,7 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
System.err.println ("Error in prefetchNodes(): "+x);
|
app.logError("Error in prefetchNodes()", x);
|
||||||
} finally {
|
} finally {
|
||||||
if (logSql) {
|
if (logSql) {
|
||||||
long logTimeStop = System.currentTimeMillis();
|
long logTimeStop = System.currentTimeMillis();
|
||||||
|
@ -1591,22 +1543,10 @@ public final class NodeManager {
|
||||||
|
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
Relation[] joins = dbm.getJoins();
|
Relation[] joins = dbm.getJoins();
|
||||||
StringBuffer b = dbm.getSelect(null).append("WHERE ")
|
|
||||||
.append(dbm.getTableName())
|
|
||||||
.append(".")
|
|
||||||
.append(idfield)
|
|
||||||
.append(" = ");
|
|
||||||
|
|
||||||
if (dbm.needsQuotes(idfield)) {
|
|
||||||
b.append("'");
|
|
||||||
b.append(escape(kstr));
|
|
||||||
b.append("'");
|
|
||||||
} else {
|
|
||||||
b.append(kstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
StringBuffer b = dbm.getSelect(null).append("WHERE ");
|
||||||
|
dbm.appendCondition(b, idfield, kstr);
|
||||||
dbm.addJoinConstraints(b, " AND ");
|
dbm.addJoinConstraints(b, " AND ");
|
||||||
|
|
||||||
query = b.toString();
|
query = b.toString();
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery(query);
|
ResultSet rs = stmt.executeQuery(query);
|
||||||
|
@ -1689,14 +1629,7 @@ public final class NodeManager {
|
||||||
if (home.getSubnodeRelation() != null && !rel.isComplexReference()) {
|
if (home.getSubnodeRelation() != null && !rel.isComplexReference()) {
|
||||||
// combine our key with the constraints in the manually set subnode relation
|
// combine our key with the constraints in the manually set subnode relation
|
||||||
b.append(" WHERE ");
|
b.append(" WHERE ");
|
||||||
if (rel.accessName.indexOf('(') == -1 && rel.accessName.indexOf('.') == -1) {
|
dbm.appendCondition(b, rel.accessName, kstr);
|
||||||
b.append(dbm.getTableName());
|
|
||||||
b.append(".");
|
|
||||||
}
|
|
||||||
b.append(rel.accessName);
|
|
||||||
b.append(" = '");
|
|
||||||
b.append(escape(kstr));
|
|
||||||
b.append("'");
|
|
||||||
// add join contraints in case this is an old oracle style join
|
// add join contraints in case this is an old oracle style join
|
||||||
dbm.addJoinConstraints(b, " AND ");
|
dbm.addJoinConstraints(b, " AND ");
|
||||||
// add potential constraints from manually set subnodeRelation
|
// add potential constraints from manually set subnodeRelation
|
||||||
|
@ -1778,7 +1711,8 @@ public final class NodeManager {
|
||||||
|
|
||||||
// set prototype?
|
// set prototype?
|
||||||
if (columns[i].isPrototypeField()) {
|
if (columns[i].isPrototypeField()) {
|
||||||
protoName = rs.getString(i+1+offset);
|
String protoId = rs.getString(i + 1 + offset);
|
||||||
|
protoName = dbm.getPrototypeName(protoId);
|
||||||
|
|
||||||
if (protoName != null) {
|
if (protoName != null) {
|
||||||
dbmap = getDbMapping(protoName);
|
dbmap = getDbMapping(protoName);
|
||||||
|
@ -1996,32 +1930,6 @@ public final class NodeManager {
|
||||||
return app.getDbMapping(protoname);
|
return app.getDbMapping(protoname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// a utility method to escape single quotes
|
|
||||||
private 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of the the keys currently held in the object cache
|
* Get an array of the the keys currently held in the object cache
|
||||||
*/
|
*/
|
||||||
|
@ -2144,7 +2052,19 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStatementValues (PreparedStatement stmt, int stmtNumber, Property p, int columnType) throws SQLException {
|
private void setStatementValue(PreparedStatement stmt, int stmtNumber, String value, DbColumn col)
|
||||||
|
throws SQLException {
|
||||||
|
if (value == null) {
|
||||||
|
stmt.setNull(stmtNumber, col.getType());
|
||||||
|
} else if (col.needsQuotes()) {
|
||||||
|
stmt.setString(stmtNumber, value);
|
||||||
|
} else {
|
||||||
|
stmt.setLong(stmtNumber, Long.parseLong(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setStatementValue(PreparedStatement stmt, int stmtNumber, Property p, int columnType)
|
||||||
|
throws SQLException {
|
||||||
if (p.getValue() == null) {
|
if (p.getValue() == null) {
|
||||||
stmt.setNull(stmtNumber, columnType);
|
stmt.setNull(stmtNumber, columnType);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -813,30 +813,17 @@ public final class Relation {
|
||||||
* and a local object.
|
* and a local object.
|
||||||
*/
|
*/
|
||||||
public String buildQuery(INode home, INode nonvirtual, String kstr, String pre,
|
public String buildQuery(INode home, INode nonvirtual, String kstr, String pre,
|
||||||
boolean useOrder) throws SQLException {
|
boolean useOrder)
|
||||||
|
throws SQLException, ClassNotFoundException {
|
||||||
StringBuffer q = new StringBuffer();
|
StringBuffer q = new StringBuffer();
|
||||||
String prefix = pre;
|
String prefix = pre;
|
||||||
|
|
||||||
if (kstr != null && !isComplexReference()) {
|
if (kstr != null && !isComplexReference()) {
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
|
|
||||||
String accessColumn = (accessName == null) ? otherType.getIDField() : accessName;
|
String accessColumn = (accessName == null) ?
|
||||||
|
otherType.getIDField() : accessName;
|
||||||
if (accessColumn.indexOf('(') == -1 && accessColumn.indexOf('.') == -1) {
|
otherType.appendCondition(q, accessColumn, kstr);
|
||||||
q.append(otherType.getTableName());
|
|
||||||
q.append(".");
|
|
||||||
}
|
|
||||||
q.append(accessColumn);
|
|
||||||
q.append(" = ");
|
|
||||||
|
|
||||||
// check if column is string type and value needs to be quoted
|
|
||||||
if (otherType.needsQuotes(accessColumn)) {
|
|
||||||
q.append("'");
|
|
||||||
q.append(escape(kstr));
|
|
||||||
q.append("'");
|
|
||||||
} else {
|
|
||||||
q.append(escape(kstr));
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix = " AND ";
|
prefix = " AND ";
|
||||||
}
|
}
|
||||||
|
@ -906,7 +893,7 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
// end column version
|
// end column version
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
q.append(escape(value.toString()));
|
q.append(DbMapping.escape(value.toString()));
|
||||||
} else {
|
} else {
|
||||||
q.append("NULL");
|
q.append("NULL");
|
||||||
}
|
}
|
||||||
|
@ -930,7 +917,7 @@ public final class Relation {
|
||||||
*/
|
*/
|
||||||
public void renderConstraints(StringBuffer q, INode home,
|
public void renderConstraints(StringBuffer q, INode home,
|
||||||
INode nonvirtual, String prefix)
|
INode nonvirtual, String prefix)
|
||||||
throws SQLException {
|
throws SQLException, ClassNotFoundException {
|
||||||
|
|
||||||
if (constraints.length > 1 && logicalOperator != AND) {
|
if (constraints.length > 1 && logicalOperator != AND) {
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
|
@ -949,6 +936,23 @@ public final class Relation {
|
||||||
prefix = " AND ";
|
prefix = " AND ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// also take the prototype into consideration if someone
|
||||||
|
// specifies an extension of an prototype inside the brakets of
|
||||||
|
// a type.properties's collection, only nodes having this proto
|
||||||
|
// sould appear inside the collection
|
||||||
|
if (otherType.inheritsStorage()) {
|
||||||
|
String protoField = otherType.getPrototypeField();
|
||||||
|
String[] extensions = otherType.getExtensions();
|
||||||
|
|
||||||
|
// extensions should never be null for extension- and
|
||||||
|
// extended prototypes. nevertheless we check it here
|
||||||
|
if (extensions != null) {
|
||||||
|
q.append(prefix);
|
||||||
|
otherType.appendCondition(q, protoField, extensions);
|
||||||
|
prefix = " AND ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
appendFilter(q, nonvirtual, prefix);
|
appendFilter(q, nonvirtual, prefix);
|
||||||
}
|
}
|
||||||
|
@ -1214,32 +1218,6 @@ public final class Relation {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -1279,7 +1257,7 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addToQuery(StringBuffer q, INode home, INode nonvirtual)
|
public void addToQuery(StringBuffer q, INode home, INode nonvirtual)
|
||||||
throws SQLException {
|
throws SQLException, ClassNotFoundException {
|
||||||
String local = null;
|
String local = null;
|
||||||
INode ref = isGroupby ? home : nonvirtual;
|
INode ref = isGroupby ? home : nonvirtual;
|
||||||
|
|
||||||
|
@ -1292,20 +1270,7 @@ public final class Relation {
|
||||||
local = ref.getString(homeprop);
|
local = ref.getString(homeprop);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foreignName.indexOf('(') == -1 && foreignName.indexOf('.') == -1) {
|
otherType.appendCondition(q, foreignName, local);
|
||||||
q.append(otherType.getTableName());
|
|
||||||
q.append(".");
|
|
||||||
}
|
|
||||||
q.append(foreignName);
|
|
||||||
q.append(" = ");
|
|
||||||
|
|
||||||
if (otherType.needsQuotes(foreignName)) {
|
|
||||||
q.append("'");
|
|
||||||
q.append(escape(local));
|
|
||||||
q.append("'");
|
|
||||||
} else {
|
|
||||||
q.append(escape(local));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean foreignKeyIsPrimary() {
|
public boolean foreignKeyIsPrimary() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue