Implement aggressive loading of object references via left joins
This commit is contained in:
parent
8b1fed03d3
commit
469399e972
4 changed files with 311 additions and 193 deletions
|
@ -77,6 +77,9 @@ public final class DbMapping implements Updatable {
|
||||||
// Map of db columns by name
|
// Map of db columns by name
|
||||||
HashMap columnMap;
|
HashMap columnMap;
|
||||||
|
|
||||||
|
// Array of aggressively loaded references
|
||||||
|
Relation[] joins;
|
||||||
|
|
||||||
// pre-rendered select statement
|
// pre-rendered select statement
|
||||||
String selectString = null;
|
String selectString = null;
|
||||||
String insertString = null;
|
String insertString = null;
|
||||||
|
@ -256,6 +259,7 @@ public final class DbMapping implements Updatable {
|
||||||
|
|
||||||
HashMap p2d = new HashMap();
|
HashMap p2d = new HashMap();
|
||||||
HashMap d2p = new HashMap();
|
HashMap d2p = new HashMap();
|
||||||
|
ArrayList joinList = new ArrayList();
|
||||||
|
|
||||||
for (Enumeration e = props.keys(); e.hasMoreElements();) {
|
for (Enumeration e = props.keys(); e.hasMoreElements();) {
|
||||||
String propName = (String) e.nextElement();
|
String propName = (String) e.nextElement();
|
||||||
|
@ -293,6 +297,13 @@ public final class DbMapping implements Updatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if a reference is aggressively fetched
|
||||||
|
if ((rel.reftype == Relation.REFERENCE ||
|
||||||
|
rel.reftype == Relation.COMPLEX_REFERENCE) &&
|
||||||
|
rel.aggressiveLoading) {
|
||||||
|
joinList.add(rel);
|
||||||
|
}
|
||||||
|
|
||||||
// app.logEvent ("Mapping "+propName+" -> "+dbField);
|
// app.logEvent ("Mapping "+propName+" -> "+dbField);
|
||||||
}
|
}
|
||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
|
@ -303,6 +314,9 @@ public final class DbMapping implements Updatable {
|
||||||
prop2db = p2d;
|
prop2db = p2d;
|
||||||
db2prop = d2p;
|
db2prop = d2p;
|
||||||
|
|
||||||
|
joins = new Relation[joinList.size()];
|
||||||
|
joins = (Relation[]) joinList.toArray(joins);
|
||||||
|
|
||||||
String subnodeMapping = props.getProperty("_children");
|
String subnodeMapping = props.getProperty("_children");
|
||||||
|
|
||||||
if (subnodeMapping != null) {
|
if (subnodeMapping != null) {
|
||||||
|
@ -844,9 +858,9 @@ public final class DbMapping implements Updatable {
|
||||||
Relation rel = columnNameToRelation(colName);
|
Relation rel = columnNameToRelation(colName);
|
||||||
|
|
||||||
DbColumn col = new DbColumn(colName, meta.getColumnType(i + 1), rel, this);
|
DbColumn col = new DbColumn(colName, meta.getColumnType(i + 1), rel, this);
|
||||||
if (col.isMapped()) {
|
// if (col.isMapped()) {
|
||||||
list.add(col);
|
list.add(col);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
columns = new DbColumn[list.size()];
|
columns = new DbColumn[list.size()];
|
||||||
columns = (DbColumn[]) list.toArray(columns);
|
columns = (DbColumn[]) list.toArray(columns);
|
||||||
|
@ -855,6 +869,13 @@ public final class DbMapping implements Updatable {
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the array of relations that are fetched with objects of this type.
|
||||||
|
*/
|
||||||
|
public Relation[] getJoins() {
|
||||||
|
return joins;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -909,7 +930,7 @@ public final class DbMapping implements Updatable {
|
||||||
|
|
||||||
StringBuffer s = new StringBuffer("SELECT ");
|
StringBuffer s = new StringBuffer("SELECT ");
|
||||||
|
|
||||||
DbColumn[] cols = columns;
|
/* DbColumn[] cols = columns;
|
||||||
|
|
||||||
if (cols == null) {
|
if (cols == null) {
|
||||||
cols = getColumns();
|
cols = getColumns();
|
||||||
|
@ -922,11 +943,28 @@ public final class DbMapping implements Updatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < joins.length; i++) {
|
||||||
|
} */
|
||||||
|
|
||||||
|
s.append ("*");
|
||||||
|
|
||||||
s.append(" FROM ");
|
s.append(" FROM ");
|
||||||
|
|
||||||
s.append(getTableName());
|
s.append(getTableName());
|
||||||
s.append(" ");
|
s.append(" ");
|
||||||
|
|
||||||
|
for (int i = 0; i < joins.length; i++) {
|
||||||
|
if (!joins[i].otherType.isRelational()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
s.append("LEFT JOIN ");
|
||||||
|
s.append(joins[i].otherType.getTableName());
|
||||||
|
s.append(" AS _HLM_");
|
||||||
|
s.append(joins[i].propName);
|
||||||
|
s.append(" ON ");
|
||||||
|
joins[i].renderJoinConstraints(s);
|
||||||
|
}
|
||||||
|
|
||||||
// cache rendered string for later calls.
|
// cache rendered string for later calls.
|
||||||
selectString = s.toString();
|
selectString = s.toString();
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ public final class Node implements INode, Serializable {
|
||||||
transient private int state;
|
transient private int state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor is only used for instances of the NullNode subclass. Do not use for ordinary Nodes.
|
* This constructor is only used for NullNode instance. Do not use for ordinary Nodes.
|
||||||
*/
|
*/
|
||||||
Node() {
|
Node() {
|
||||||
created = lastmodified = System.currentTimeMillis();
|
created = lastmodified = System.currentTimeMillis();
|
||||||
|
@ -145,197 +145,28 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor used for nodes being stored in a relational database table.
|
* Initializer used for nodes being stored in a relational database table.
|
||||||
*/
|
*/
|
||||||
public Node(DbMapping dbm, ResultSet rs, DbColumn[] columns, WrappedNodeManager nmgr)
|
public void init(DbMapping dbm, String id, String name, String protoName,
|
||||||
throws SQLException, IOException {
|
Hashtable propMap, WrappedNodeManager nmgr) {
|
||||||
this.nmgr = nmgr;
|
this.nmgr = nmgr;
|
||||||
|
|
||||||
// see what prototype/DbMapping this object should use
|
// see what prototype/DbMapping this object should use
|
||||||
dbmap = dbm;
|
this.dbmap = dbm;
|
||||||
|
// set the prototype name
|
||||||
|
this.prototype = protoName;
|
||||||
|
|
||||||
created = lastmodified = System.currentTimeMillis();
|
this.id = id;
|
||||||
|
|
||||||
for (int i = 0; i < columns.length; i++) {
|
|
||||||
|
|
||||||
// set prototype?
|
|
||||||
if (columns[i].isPrototypeField()) {
|
|
||||||
String protoName = rs.getString(i+1);
|
|
||||||
|
|
||||||
if (protoName != null) {
|
|
||||||
dbmap = nmgr.getDbMapping(protoName);
|
|
||||||
|
|
||||||
if (dbmap == null) {
|
|
||||||
// invalid prototype name!
|
|
||||||
System.err.println("Warning: Invalid prototype name: " + protoName +
|
|
||||||
" - using default");
|
|
||||||
dbmap = dbm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set id?
|
|
||||||
if (columns[i].isIdField()) {
|
|
||||||
id = rs.getString(i+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set name?
|
|
||||||
if (columns[i].isNameField()) {
|
|
||||||
name = rs.getString(i+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Relation rel = columns[i].getRelation();
|
|
||||||
|
|
||||||
if ((rel == null) ||
|
|
||||||
((rel.reftype != Relation.PRIMITIVE) &&
|
|
||||||
(rel.reftype != Relation.REFERENCE))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Property newprop = new Property(rel.propName, this);
|
|
||||||
|
|
||||||
switch (columns[i].getType()) {
|
|
||||||
case Types.BIT:
|
|
||||||
newprop.setBooleanValue(rs.getBoolean(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.TINYINT:
|
|
||||||
case Types.BIGINT:
|
|
||||||
case Types.SMALLINT:
|
|
||||||
case Types.INTEGER:
|
|
||||||
newprop.setIntegerValue(rs.getLong(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.REAL:
|
|
||||||
case Types.FLOAT:
|
|
||||||
case Types.DOUBLE:
|
|
||||||
newprop.setFloatValue(rs.getDouble(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.DECIMAL:
|
|
||||||
case Types.NUMERIC:
|
|
||||||
|
|
||||||
BigDecimal num = rs.getBigDecimal(i+1);
|
|
||||||
|
|
||||||
if (num == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num.scale() > 0) {
|
|
||||||
newprop.setFloatValue(num.doubleValue());
|
|
||||||
} else {
|
|
||||||
newprop.setIntegerValue(num.longValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.VARBINARY:
|
|
||||||
case Types.BINARY:
|
|
||||||
newprop.setStringValue(rs.getString(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.LONGVARBINARY:
|
|
||||||
case Types.LONGVARCHAR:
|
|
||||||
|
|
||||||
try {
|
|
||||||
newprop.setStringValue(rs.getString(i+1));
|
|
||||||
} catch (SQLException x) {
|
|
||||||
Reader in = rs.getCharacterStream(i+1);
|
|
||||||
char[] buffer = new char[2048];
|
|
||||||
int read = 0;
|
|
||||||
int r = 0;
|
|
||||||
|
|
||||||
while ((r = in.read(buffer, read, buffer.length - read)) > -1) {
|
|
||||||
read += r;
|
|
||||||
|
|
||||||
if (read == buffer.length) {
|
|
||||||
// grow input buffer
|
|
||||||
char[] newBuffer = new char[buffer.length * 2];
|
|
||||||
|
|
||||||
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
|
||||||
buffer = newBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newprop.setStringValue(new String(buffer, 0, read));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.CHAR:
|
|
||||||
case Types.VARCHAR:
|
|
||||||
case Types.OTHER:
|
|
||||||
newprop.setStringValue(rs.getString(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.DATE:
|
|
||||||
newprop.setDateValue(rs.getDate(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.TIME:
|
|
||||||
newprop.setDateValue(rs.getTime(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.TIMESTAMP:
|
|
||||||
newprop.setDateValue(rs.getTimestamp(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.NULL:
|
|
||||||
newprop.setStringValue(null);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
// continue;
|
|
||||||
default:
|
|
||||||
newprop.setStringValue(rs.getString(i+1));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rs.wasNull()) {
|
|
||||||
newprop.setStringValue(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propMap == null) {
|
|
||||||
propMap = new Hashtable();
|
|
||||||
}
|
|
||||||
|
|
||||||
propMap.put(rel.propName.toLowerCase(), newprop);
|
|
||||||
|
|
||||||
// if the property is a pointer to another node, change the property type to NODE
|
|
||||||
if ((rel.reftype == Relation.REFERENCE) && rel.usesPrimaryKey()) {
|
|
||||||
// FIXME: References to anything other than the primary key are not supported
|
|
||||||
newprop.convertToNodeReference(rel.otherType);
|
|
||||||
|
|
||||||
// newprop.nhandle = new NodeHandle (new DbKey (rel.otherType, newprop.getStringValue ()));
|
|
||||||
// newprop.type = IProperty.NODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark property as clean, since it's fresh from the db
|
|
||||||
newprop.dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the prototype from the dbmap,
|
|
||||||
// which was possibly modified while reading the resultset
|
|
||||||
setPrototype(dbmap.getTypeName());
|
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
// If name was not set from resultset, create a synthetical name now.
|
// If name was not set from resultset, create a synthetical name now.
|
||||||
if ((name == null) || (name.length() == 0)) {
|
if ((name == null) || (name.length() == 0)) {
|
||||||
name = dbmap.getTypeName() + " " + id;
|
this.name = dbmap.getTypeName() + " " + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// again set created and lastmodified. This is because
|
this.propMap = propMap;
|
||||||
// lastmodified has been been updated, and we want both values to
|
|
||||||
// be identical to show that the node hasn't been changed since
|
// set lastmodified and created timestamps and mark as clean
|
||||||
// it was first created.
|
|
||||||
created = lastmodified = System.currentTimeMillis();
|
created = lastmodified = System.currentTimeMillis();
|
||||||
markAs(CLEAN);
|
markAs(CLEAN);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package helma.objectmodel.db;
|
||||||
import helma.framework.core.Application;
|
import helma.framework.core.Application;
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.*;
|
||||||
import helma.util.CacheMap;
|
import helma.util.CacheMap;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -972,6 +973,7 @@ public final class NodeManager {
|
||||||
public List getNodeIDs(Node home, Relation rel) throws Exception {
|
public List getNodeIDs(Node home, Relation rel) throws Exception {
|
||||||
// Transactor tx = (Transactor) Thread.currentThread ();
|
// Transactor tx = (Transactor) Thread.currentThread ();
|
||||||
// tx.timer.beginEvent ("getNodeIDs "+home);
|
// tx.timer.beginEvent ("getNodeIDs "+home);
|
||||||
|
|
||||||
if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) {
|
if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) {
|
||||||
// this should never be called for embedded nodes
|
// this should never be called for embedded nodes
|
||||||
throw new RuntimeException("NodeMgr.getNodeIDs called for non-relational node " +
|
throw new RuntimeException("NodeMgr.getNodeIDs called for non-relational node " +
|
||||||
|
@ -1114,7 +1116,10 @@ public final class NodeManager {
|
||||||
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
// create new Nodes.
|
// create new Nodes.
|
||||||
Node node = new Node(rel.otherType, rs, columns, safe);
|
Node node = createNode(rel.otherType, rs, columns, 0);
|
||||||
|
if (node == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Key primKey = node.getKey();
|
Key primKey = node.getKey();
|
||||||
|
|
||||||
retval.add(new NodeHandle(primKey));
|
retval.add(new NodeHandle(primKey));
|
||||||
|
@ -1159,6 +1164,7 @@ public final class NodeManager {
|
||||||
Connection con = dbm.getConnection();
|
Connection con = dbm.getConnection();
|
||||||
Statement stmt = con.createStatement();
|
Statement stmt = con.createStatement();
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
|
Relation[] joins = dbm.getJoins();
|
||||||
StringBuffer q = dbm.getSelect();
|
StringBuffer q = dbm.getSelect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1166,6 +1172,8 @@ public final class NodeManager {
|
||||||
boolean needsQuotes = dbm.needsQuotes(idfield);
|
boolean needsQuotes = dbm.needsQuotes(idfield);
|
||||||
|
|
||||||
q.append("WHERE ");
|
q.append("WHERE ");
|
||||||
|
q.append(dbm.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(idfield);
|
q.append(idfield);
|
||||||
q.append(" IN (");
|
q.append(" IN (");
|
||||||
|
|
||||||
|
@ -1224,7 +1232,10 @@ public final class NodeManager {
|
||||||
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
// create new Nodes.
|
// create new Nodes.
|
||||||
Node node = new Node(dbm, rs, columns, safe);
|
Node node = createNode(dbm, rs, columns, 0);
|
||||||
|
if (node == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Key primKey = node.getKey();
|
Key primKey = node.getKey();
|
||||||
|
|
||||||
// for grouped nodes, collect subnode lists for the intermediary
|
// for grouped nodes, collect subnode lists for the intermediary
|
||||||
|
@ -1272,6 +1283,28 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int resultSetOffset = columns.length;
|
||||||
|
// create joined objects
|
||||||
|
for (int i = 0; i < joins.length; i++) {
|
||||||
|
DbMapping jdbm = joins[i].otherType;
|
||||||
|
node = createNode(jdbm, rs, jdbm.getColumns(), resultSetOffset);
|
||||||
|
if (node != null) {
|
||||||
|
primKey = node.getKey();
|
||||||
|
// register new nodes with the cache. If an up-to-date copy
|
||||||
|
// existed in the cache, use that.
|
||||||
|
synchronized (cache) {
|
||||||
|
Node oldnode = (Node) cache.put(primKey, node);
|
||||||
|
|
||||||
|
if ((oldnode != null) &&
|
||||||
|
(oldnode.getState() != INode.INVALID)) {
|
||||||
|
// found an ok version in the cache, use it.
|
||||||
|
cache.put(primKey, oldnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultSetOffset += jdbm.getColumns().length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If these are grouped nodes, build the intermediary group nodes
|
// If these are grouped nodes, build the intermediary group nodes
|
||||||
|
@ -1292,6 +1325,8 @@ public final class NodeManager {
|
||||||
groupnode.lastSubnodeFetch = System.currentTimeMillis();
|
groupnode.lastSubnodeFetch = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception x) {
|
||||||
|
System.err.println ("ERROR IN PREFETCHNODES: "+x);
|
||||||
} finally {
|
} finally {
|
||||||
if (stmt != null) {
|
if (stmt != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -1455,6 +1490,8 @@ public final class NodeManager {
|
||||||
StringBuffer q = dbm.getSelect();
|
StringBuffer q = dbm.getSelect();
|
||||||
|
|
||||||
q.append("WHERE ");
|
q.append("WHERE ");
|
||||||
|
q.append(dbm.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(idfield);
|
q.append(idfield);
|
||||||
q.append(" = ");
|
q.append(" = ");
|
||||||
|
|
||||||
|
@ -1476,7 +1513,7 @@ public final class NodeManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = new Node(dbm, rs, columns, safe);
|
node = createNode(dbm, rs, columns, 0);
|
||||||
|
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
throw new RuntimeException("More than one value returned by query.");
|
throw new RuntimeException("More than one value returned by query.");
|
||||||
|
@ -1538,12 +1575,15 @@ 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
|
||||||
q.append("WHERE ");
|
q.append("WHERE ");
|
||||||
|
q.append(dbm.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(rel.accessName);
|
q.append(rel.accessName);
|
||||||
q.append(" = '");
|
q.append(" = '");
|
||||||
q.append(escape(kstr));
|
q.append(escape(kstr));
|
||||||
q.append("'");
|
q.append("'");
|
||||||
q.append(" AND ");
|
q.append(" AND (");
|
||||||
q.append(home.getSubnodeRelation().trim().substring(5));
|
q.append(home.getSubnodeRelation().trim().substring(5));
|
||||||
|
q.append(")");
|
||||||
} else {
|
} else {
|
||||||
q.append(rel.buildQuery(home, home.getNonVirtualParent(), kstr,
|
q.append(rel.buildQuery(home, home.getNonVirtualParent(), kstr,
|
||||||
"WHERE ", false));
|
"WHERE ", false));
|
||||||
|
@ -1561,7 +1601,7 @@ public final class NodeManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = new Node(rel.otherType, rs, columns, safe);
|
node = createNode(rel.otherType, rs, columns, 0);
|
||||||
|
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
throw new RuntimeException("More than one value returned by query.");
|
throw new RuntimeException("More than one value returned by query.");
|
||||||
|
@ -1589,6 +1629,193 @@ public final class NodeManager {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Node from a ResultSet.
|
||||||
|
*/
|
||||||
|
public Node createNode(DbMapping dbm, ResultSet rs, DbColumn[] columns, int offset)
|
||||||
|
throws SQLException, IOException {
|
||||||
|
Hashtable propMap = new Hashtable();
|
||||||
|
String id = null;
|
||||||
|
String name = null;
|
||||||
|
String protoName = dbm.getTypeName();
|
||||||
|
DbMapping dbmap = dbm;
|
||||||
|
|
||||||
|
Node node = new Node();
|
||||||
|
|
||||||
|
for (int i = 0; i < columns.length; i++) {
|
||||||
|
|
||||||
|
// set prototype?
|
||||||
|
if (columns[i].isPrototypeField()) {
|
||||||
|
protoName = rs.getString(i+1+offset);
|
||||||
|
|
||||||
|
if (protoName != null) {
|
||||||
|
dbmap = getDbMapping(protoName);
|
||||||
|
|
||||||
|
if (dbmap == null) {
|
||||||
|
// invalid prototype name!
|
||||||
|
System.err.println("Warning: Invalid prototype name: " + protoName +
|
||||||
|
" - using default");
|
||||||
|
dbmap = dbm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set id?
|
||||||
|
if (columns[i].isIdField()) {
|
||||||
|
id = rs.getString(i+1+offset);
|
||||||
|
// if id == null, the object doesn't actually exist - return null
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set name?
|
||||||
|
if (columns[i].isNameField()) {
|
||||||
|
name = rs.getString(i+1+offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Relation rel = columns[i].getRelation();
|
||||||
|
|
||||||
|
if ((rel == null) ||
|
||||||
|
((rel.reftype != Relation.PRIMITIVE) &&
|
||||||
|
(rel.reftype != Relation.REFERENCE))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Property newprop = new Property(rel.propName, node);
|
||||||
|
|
||||||
|
switch (columns[i].getType()) {
|
||||||
|
case Types.BIT:
|
||||||
|
newprop.setBooleanValue(rs.getBoolean(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.TINYINT:
|
||||||
|
case Types.BIGINT:
|
||||||
|
case Types.SMALLINT:
|
||||||
|
case Types.INTEGER:
|
||||||
|
newprop.setIntegerValue(rs.getLong(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.REAL:
|
||||||
|
case Types.FLOAT:
|
||||||
|
case Types.DOUBLE:
|
||||||
|
newprop.setFloatValue(rs.getDouble(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.DECIMAL:
|
||||||
|
case Types.NUMERIC:
|
||||||
|
|
||||||
|
BigDecimal num = rs.getBigDecimal(i+1+offset);
|
||||||
|
|
||||||
|
if (num == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num.scale() > 0) {
|
||||||
|
newprop.setFloatValue(num.doubleValue());
|
||||||
|
} else {
|
||||||
|
newprop.setIntegerValue(num.longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.VARBINARY:
|
||||||
|
case Types.BINARY:
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.LONGVARBINARY:
|
||||||
|
case Types.LONGVARCHAR:
|
||||||
|
|
||||||
|
try {
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
} catch (SQLException x) {
|
||||||
|
Reader in = rs.getCharacterStream(i+1+offset);
|
||||||
|
char[] buffer = new char[2048];
|
||||||
|
int read = 0;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
while ((r = in.read(buffer, read, buffer.length - read)) > -1) {
|
||||||
|
read += r;
|
||||||
|
|
||||||
|
if (read == buffer.length) {
|
||||||
|
// grow input buffer
|
||||||
|
char[] newBuffer = new char[buffer.length * 2];
|
||||||
|
|
||||||
|
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
||||||
|
buffer = newBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newprop.setStringValue(new String(buffer, 0, read));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.CHAR:
|
||||||
|
case Types.VARCHAR:
|
||||||
|
case Types.OTHER:
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.DATE:
|
||||||
|
newprop.setDateValue(rs.getDate(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.TIME:
|
||||||
|
newprop.setDateValue(rs.getTime(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.TIMESTAMP:
|
||||||
|
newprop.setDateValue(rs.getTimestamp(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.NULL:
|
||||||
|
newprop.setStringValue(null);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// continue;
|
||||||
|
default:
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs.wasNull()) {
|
||||||
|
newprop.setStringValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
propMap.put(rel.propName.toLowerCase(), newprop);
|
||||||
|
|
||||||
|
// if the property is a pointer to another node, change the property type to NODE
|
||||||
|
if ((rel.reftype == Relation.REFERENCE) && rel.usesPrimaryKey()) {
|
||||||
|
// FIXME: References to anything other than the primary key are not supported
|
||||||
|
newprop.convertToNodeReference(rel.otherType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark property as clean, since it's fresh from the db
|
||||||
|
newprop.dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.init(dbmap, id, name, protoName, propMap, safe);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a DbMapping for a given prototype name. This is just a proxy
|
* Get a DbMapping for a given prototype name. This is just a proxy
|
||||||
* method to the app's getDbMapping() method.
|
* method to the app's getDbMapping() method.
|
||||||
|
|
|
@ -626,6 +626,8 @@ public final class Relation {
|
||||||
|
|
||||||
String accessColumn = (accessName == null) ? otherType.getIDField() : accessName;
|
String accessColumn = (accessName == null) ? otherType.getIDField() : accessName;
|
||||||
|
|
||||||
|
q.append(otherType.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(accessColumn);
|
q.append(accessColumn);
|
||||||
q.append(" = ");
|
q.append(" = ");
|
||||||
|
|
||||||
|
@ -678,21 +680,39 @@ public final class Relation {
|
||||||
public String renderConstraints(INode home, INode nonvirtual)
|
public String renderConstraints(INode home, INode nonvirtual)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
StringBuffer q = new StringBuffer();
|
StringBuffer q = new StringBuffer();
|
||||||
String suffix = " AND ";
|
String prefix = " AND ";
|
||||||
|
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
q.append(prefix);
|
||||||
constraints[i].addToQuery(q, home, nonvirtual);
|
constraints[i].addToQuery(q, home, nonvirtual);
|
||||||
q.append(suffix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
|
q.append(prefix);
|
||||||
q.append(filter);
|
q.append(filter);
|
||||||
q.append(suffix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.toString();
|
return q.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renderJoinConstraints(StringBuffer select) {
|
||||||
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
select.append(ownType.getTableName());
|
||||||
|
select.append(".");
|
||||||
|
select.append(constraints[i].localName);
|
||||||
|
select.append(" = _HLM_");
|
||||||
|
select.append(propName);
|
||||||
|
select.append(".");
|
||||||
|
select.append(constraints[i].foreignName);
|
||||||
|
if (i == constraints.length-1) {
|
||||||
|
select.append(" ");
|
||||||
|
} else {
|
||||||
|
select.append(" AND ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the order section to use for this relation
|
* Get the order section to use for this relation
|
||||||
*/
|
*/
|
||||||
|
@ -966,6 +986,8 @@ public final class Relation {
|
||||||
local = ref.getString(homeprop);
|
local = ref.getString(homeprop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
q.append(otherType.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(foreignName);
|
q.append(foreignName);
|
||||||
q.append(" = ");
|
q.append(" = ");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue