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 | ||||
|     HashMap columnMap; | ||||
| 
 | ||||
|     // Array of aggressively loaded references | ||||
|     Relation[] joins; | ||||
| 
 | ||||
|     // pre-rendered select statement | ||||
|     String selectString = null; | ||||
|     String insertString = null; | ||||
|  | @ -256,6 +259,7 @@ public final class DbMapping implements Updatable { | |||
| 
 | ||||
|         HashMap p2d = new HashMap(); | ||||
|         HashMap d2p = new HashMap(); | ||||
|         ArrayList joinList = new ArrayList(); | ||||
| 
 | ||||
|         for (Enumeration e = props.keys(); e.hasMoreElements();) { | ||||
|             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); | ||||
|                 } | ||||
|             } catch (Exception x) { | ||||
|  | @ -303,6 +314,9 @@ public final class DbMapping implements Updatable { | |||
|         prop2db = p2d; | ||||
|         db2prop = d2p; | ||||
| 
 | ||||
|         joins = new Relation[joinList.size()]; | ||||
|         joins = (Relation[]) joinList.toArray(joins); | ||||
| 
 | ||||
|         String subnodeMapping = props.getProperty("_children"); | ||||
| 
 | ||||
|         if (subnodeMapping != null) { | ||||
|  | @ -844,9 +858,9 @@ public final class DbMapping implements Updatable { | |||
|                 Relation rel = columnNameToRelation(colName); | ||||
| 
 | ||||
|                 DbColumn col = new DbColumn(colName, meta.getColumnType(i + 1), rel, this); | ||||
|                 if (col.isMapped()) { | ||||
|                 // if (col.isMapped()) { | ||||
|                     list.add(col); | ||||
|                 } | ||||
|                 // } | ||||
|             } | ||||
|             columns = new DbColumn[list.size()]; | ||||
|             columns = (DbColumn[]) list.toArray(columns); | ||||
|  | @ -855,6 +869,13 @@ public final class DbMapping implements Updatable { | |||
|         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 "); | ||||
| 
 | ||||
|         DbColumn[] cols = columns; | ||||
|         /* DbColumn[] cols = columns; | ||||
| 
 | ||||
|         if (cols == null) { | ||||
|             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(getTableName()); | ||||
|         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. | ||||
|         selectString = s.toString(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -76,7 +76,7 @@ public final class Node implements INode, Serializable { | |||
|     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() { | ||||
|         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) | ||||
|             throws SQLException, IOException { | ||||
|     public void init(DbMapping dbm, String id, String name, String protoName, | ||||
|                 Hashtable propMap, WrappedNodeManager nmgr) { | ||||
|         this.nmgr = nmgr; | ||||
| 
 | ||||
|         // see what prototype/DbMapping this object should use | ||||
|         dbmap = dbm; | ||||
|         this.dbmap = dbm; | ||||
|         // set the prototype name | ||||
|         this.prototype = protoName; | ||||
| 
 | ||||
|         created = lastmodified = System.currentTimeMillis(); | ||||
| 
 | ||||
|         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.id = id; | ||||
| 
 | ||||
|         this.name = name; | ||||
|         // If name was not set from resultset, create a synthetical name now. | ||||
|         if ((name == null) || (name.length() == 0)) { | ||||
|             name = dbmap.getTypeName() + " " + id; | ||||
|             this.name = dbmap.getTypeName() + " " + id; | ||||
|         } | ||||
| 
 | ||||
|         // again set created and lastmodified. This is because | ||||
|         // lastmodified has been been updated, and we want both values to | ||||
|         // be identical to show that the node hasn't been changed since | ||||
|         // it was first created. | ||||
|         this.propMap = propMap; | ||||
| 
 | ||||
|         // set lastmodified and created timestamps and mark as clean | ||||
|         created = lastmodified = System.currentTimeMillis(); | ||||
|         markAs(CLEAN); | ||||
|     } | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ package helma.objectmodel.db; | |||
| import helma.framework.core.Application; | ||||
| import helma.objectmodel.*; | ||||
| import helma.util.CacheMap; | ||||
| import java.math.BigDecimal; | ||||
| import java.io.*; | ||||
| import java.sql.*; | ||||
| import java.util.*; | ||||
|  | @ -972,6 +973,7 @@ public final class NodeManager { | |||
|     public List getNodeIDs(Node home, Relation rel) throws Exception { | ||||
|         // Transactor tx = (Transactor) Thread.currentThread (); | ||||
|         // tx.timer.beginEvent ("getNodeIDs "+home); | ||||
| 
 | ||||
|         if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) { | ||||
|             // this should never be called for embedded nodes | ||||
|             throw new RuntimeException("NodeMgr.getNodeIDs called for non-relational node " + | ||||
|  | @ -1114,7 +1116,10 @@ public final class NodeManager { | |||
| 
 | ||||
|                 while (rs.next()) { | ||||
|                     // 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(); | ||||
| 
 | ||||
|                     retval.add(new NodeHandle(primKey)); | ||||
|  | @ -1159,6 +1164,7 @@ public final class NodeManager { | |||
|                 Connection con = dbm.getConnection(); | ||||
|                 Statement stmt = con.createStatement(); | ||||
|                 DbColumn[] columns = dbm.getColumns(); | ||||
|                 Relation[] joins = dbm.getJoins(); | ||||
|                 StringBuffer q = dbm.getSelect(); | ||||
| 
 | ||||
|                 try { | ||||
|  | @ -1166,6 +1172,8 @@ public final class NodeManager { | |||
|                     boolean needsQuotes = dbm.needsQuotes(idfield); | ||||
| 
 | ||||
|                     q.append("WHERE "); | ||||
|                     q.append(dbm.getTableName()); | ||||
|                     q.append("."); | ||||
|                     q.append(idfield); | ||||
|                     q.append(" IN ("); | ||||
| 
 | ||||
|  | @ -1224,7 +1232,10 @@ public final class NodeManager { | |||
| 
 | ||||
|                     while (rs.next()) { | ||||
|                         // 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(); | ||||
| 
 | ||||
|                         // 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 | ||||
|  | @ -1292,6 +1325,8 @@ public final class NodeManager { | |||
|                             groupnode.lastSubnodeFetch = System.currentTimeMillis(); | ||||
|                         } | ||||
|                     } | ||||
|                 } catch (Exception x) { | ||||
|                     System.err.println ("ERROR IN PREFETCHNODES: "+x); | ||||
|                 } finally { | ||||
|                     if (stmt != null) { | ||||
|                         try { | ||||
|  | @ -1455,6 +1490,8 @@ public final class NodeManager { | |||
|                 StringBuffer q = dbm.getSelect(); | ||||
| 
 | ||||
|                 q.append("WHERE "); | ||||
|                 q.append(dbm.getTableName()); | ||||
|                 q.append("."); | ||||
|                 q.append(idfield); | ||||
|                 q.append(" = "); | ||||
| 
 | ||||
|  | @ -1476,7 +1513,7 @@ public final class NodeManager { | |||
|                     return null; | ||||
|                 } | ||||
| 
 | ||||
|                 node = new Node(dbm, rs, columns, safe); | ||||
|                 node = createNode(dbm, rs, columns, 0); | ||||
| 
 | ||||
|                 if (rs.next()) { | ||||
|                     throw new RuntimeException("More than one value returned by query."); | ||||
|  | @ -1538,12 +1575,15 @@ public final class NodeManager { | |||
|                 if (home.getSubnodeRelation() != null && !rel.isComplexReference()) { | ||||
|                     // combine our key with the constraints in the manually set subnode relation | ||||
|                     q.append("WHERE "); | ||||
|                     q.append(dbm.getTableName()); | ||||
|                     q.append("."); | ||||
|                     q.append(rel.accessName); | ||||
|                     q.append(" = '"); | ||||
|                     q.append(escape(kstr)); | ||||
|                     q.append("'"); | ||||
|                     q.append(" AND "); | ||||
|                     q.append(" AND ("); | ||||
|                     q.append(home.getSubnodeRelation().trim().substring(5)); | ||||
|                     q.append(")"); | ||||
|                 } else { | ||||
|                     q.append(rel.buildQuery(home, home.getNonVirtualParent(), kstr, | ||||
|                                             "WHERE ", false)); | ||||
|  | @ -1561,7 +1601,7 @@ public final class NodeManager { | |||
|                     return null; | ||||
|                 } | ||||
| 
 | ||||
|                 node = new Node(rel.otherType, rs, columns, safe); | ||||
|                 node = createNode(rel.otherType, rs, columns, 0); | ||||
| 
 | ||||
|                 if (rs.next()) { | ||||
|                     throw new RuntimeException("More than one value returned by query."); | ||||
|  | @ -1589,6 +1629,193 @@ public final class NodeManager { | |||
|         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 | ||||
|      * method to the app's getDbMapping() method. | ||||
|  |  | |||
|  | @ -626,6 +626,8 @@ public final class Relation { | |||
| 
 | ||||
|             String accessColumn = (accessName == null) ? otherType.getIDField() : accessName; | ||||
| 
 | ||||
|             q.append(otherType.getTableName()); | ||||
|             q.append("."); | ||||
|             q.append(accessColumn); | ||||
|             q.append(" = "); | ||||
| 
 | ||||
|  | @ -678,21 +680,39 @@ public final class Relation { | |||
|     public String renderConstraints(INode home, INode nonvirtual) | ||||
|                              throws SQLException { | ||||
|         StringBuffer q = new StringBuffer(); | ||||
|         String suffix = " AND "; | ||||
|         String prefix = " AND "; | ||||
| 
 | ||||
|         for (int i = 0; i < constraints.length; i++) { | ||||
|             q.append(prefix); | ||||
|             constraints[i].addToQuery(q, home, nonvirtual); | ||||
|             q.append(suffix); | ||||
|         } | ||||
| 
 | ||||
|         if (filter != null) { | ||||
|             q.append(prefix); | ||||
|             q.append(filter); | ||||
|             q.append(suffix); | ||||
|         } | ||||
| 
 | ||||
|         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 | ||||
|      */ | ||||
|  | @ -966,6 +986,8 @@ public final class Relation { | |||
|                 local = ref.getString(homeprop); | ||||
|             } | ||||
| 
 | ||||
|             q.append(otherType.getTableName()); | ||||
|             q.append("."); | ||||
|             q.append(foreignName); | ||||
|             q.append(" = "); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue