From dfc243134325de5ce353d96d51666aa74944b74e Mon Sep 17 00:00:00 2001 From: hns Date: Wed, 6 Nov 2002 10:49:41 +0000 Subject: [PATCH] Use JDBC API directly for all queries. The only place where Village is still used is for inserts and updates. --- src/helma/objectmodel/db/DbMapping.java | 84 +++++++-- src/helma/objectmodel/db/Node.java | 47 +++--- src/helma/objectmodel/db/NodeManager.java | 197 +++++++++++----------- src/helma/objectmodel/db/Relation.java | 11 ++ 4 files changed, 207 insertions(+), 132 deletions(-) diff --git a/src/helma/objectmodel/db/DbMapping.java b/src/helma/objectmodel/db/DbMapping.java index f065d319..a2f48c8d 100644 --- a/src/helma/objectmodel/db/DbMapping.java +++ b/src/helma/objectmodel/db/DbMapping.java @@ -52,6 +52,10 @@ public final class DbMapping implements Updatable { HashMap prop2db; // Map of db columns to Relations objects HashMap db2prop; + // prerendered list of columns to fetch from db + String columns = null; + // pre-rendered select statement + String select = null; // db field used as primary key private String idField; @@ -135,7 +139,9 @@ public final class DbMapping implements Updatable { * for rewire to work, all other db mappings must have been initialized and registered. */ public synchronized void update () { - + // reset columns + columns = select = null; + // read in properties table = props.getProperty ("_table"); idgen = props.getProperty ("_idgen"); // see if there is a field which specifies the prototype of objects, if different prototypes @@ -552,16 +558,66 @@ public final class DbMapping implements Updatable { * Return a Village Schema object for this DbMapping. */ public synchronized Schema getSchema () throws ClassNotFoundException, SQLException, DataSetException { + if (!isRelational ()) + throw new SQLException ("Can't get Schema for non-relational data mapping"); + if (source == null && parentMapping != null) + return parentMapping.getSchema (); + // Use local variable s to avoid synchronization (schema may be nulled elsewhere) + Schema s = schema; + if (s != null) + return s; + schema = new Schema ().schema (getConnection (), table, "*"); + return schema; + } + + + /** + * Return a Village Schema object for this DbMapping. + */ + public synchronized String getColumns() throws ClassNotFoundException, SQLException { if (!isRelational ()) throw new SQLException ("Can't get Schema for non-relational data mapping"); if (source == null && parentMapping != null) - return parentMapping.getSchema (); + return parentMapping.getColumns (); // Use local variable s to avoid synchronization (schema may be nulled elsewhere) - Schema s = schema; - if (s != null) - return s; - schema = new Schema ().schema (getConnection (), table, "*"); - return schema; + if (columns == null) { + // we do two things here: set the SQL type on the Relation mappings + // and build a string of column names. + Connection con = getConnection (); + Statement stmt = con.createStatement (); + ResultSet rs = stmt.executeQuery ("select * from "+getTableName()+" where 1 = 0"); + if (rs == null) + throw new SQLException ("Error retrieving DB scheme for "+this); + ResultSetMetaData meta = rs.getMetaData (); + // ok, we have the meta data, now loop through mapping... + // StringBuffer cbuffer = new StringBuffer (getIDField ()); + for (Iterator i=getDBPropertyIterator(); i.hasNext(); ) { + Relation rel = (Relation) i.next (); + if (rel.reftype != Relation.PRIMITIVE && rel.reftype != Relation.REFERENCE) + continue; + // cbuffer.append (","); + // cbuffer.append (rel.getDbField()); + int idx = rs.findColumn (rel.getDbField()); + rel.setColumnType (meta.getColumnType (idx)); + } + // columns = cbuffer.toString(); + columns = " * "; + } + return columns; + } + + public StringBuffer getSelect () throws SQLException, ClassNotFoundException { + String sel = select; + if (sel != null) + return new StringBuffer (sel); + StringBuffer s = new StringBuffer ("select "); + s.append (getColumns ()); + s.append (" from "); + s.append (table); + s.append (" "); + // cache rendered string for later calls. + select = s.toString(); + return s; } /** @@ -569,14 +625,16 @@ public final class DbMapping implements Updatable { * to be quoted in SQL queries. */ public boolean needsQuotes (String columnName) throws SQLException { + if (table == null && parentMapping != null) + return parentMapping.needsQuotes (columnName); try { - Schema s = getSchema (); - if (s == null) + Relation rel = (Relation) db2prop.get (columnName); + if (rel == null) throw new SQLException ("Error retrieving relational schema for "+this); - Column c = s.getColumn (columnName); - if (c == null) - throw new SQLException ("Column "+columnName+" not found in "+this); - switch (c.typeEnum()) { + // make sure columns are initialized and up to date + if (columns == null) + getColumns(); + switch (rel.getColumnType()) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: diff --git a/src/helma/objectmodel/db/Node.java b/src/helma/objectmodel/db/Node.java index e9289771..3bbf5ac3 100644 --- a/src/helma/objectmodel/db/Node.java +++ b/src/helma/objectmodel/db/Node.java @@ -12,11 +12,11 @@ import java.util.Date; import java.util.List; import java.util.StringTokenizer; import java.io.*; -import java.sql.Types; +import java.sql.*; import helma.objectmodel.*; import helma.util.*; import helma.framework.IPathElement; -import com.workingdogs.village.*; +// import com.workingdogs.village.*; /** @@ -234,16 +234,15 @@ public final class Node implements INode, Serializable { /** * Constructor used for nodes being stored in a relational database table. */ - public Node (DbMapping dbm, Record rec, WrappedNodeManager nmgr) throws DataSetException { + public Node (DbMapping dbm, ResultSet rs, WrappedNodeManager nmgr) throws SQLException { this.nmgr = nmgr; // see what prototype/DbMapping this object should use dbmap = dbm; String protoField= dbmap.getPrototypeField (); if (protoField != null) { - Value val = rec.getValue (protoField); - if (val != null && !val.isNull ()) { - String protoName = val.asString (); + String protoName = rs.getString (protoField); + if (protoName != null) { dbmap = nmgr.getDbMapping (protoName); if (dbmap == null) { // invalid prototype name! @@ -254,10 +253,10 @@ public final class Node implements INode, Serializable { } setPrototype (dbmap.getTypeName ()); - id = rec.getValue (dbmap.getIDField ()).asString (); + id = rs.getString (dbmap.getIDField ()); // checkWriteLock (); String nameField = dbmap.getNameField (); - name = nameField == null ? id : rec.getValue (nameField).asString (); + name = nameField == null ? id : rs.getString (nameField); if (name == null || name.length() == 0) name = dbmap.getTypeName() + " " + id; // set parent for user objects to internal userroot node @@ -276,60 +275,61 @@ public final class Node implements INode, Serializable { if (rel.reftype != Relation.PRIMITIVE && rel.reftype != Relation.REFERENCE) continue; - Value val = rec.getValue (rel.getDbField ()); + // Value val = rec.getValue (rel.getDbField ()); // if (val.isNull ()) // continue; Property newprop = new Property (rel.propName, this); - if (val.isNull ()) - newprop.setStringValue (null); - else switch (val.type ()) { + // if (val.isNull ()) + // newprop.setStringValue (null); + // else + switch (rel.getColumnType()) { case Types.BIT: - newprop.setBooleanValue (val.asBoolean()); + newprop.setBooleanValue (rs.getBoolean(rel.getDbField())); break; case Types.TINYINT: case Types.BIGINT: case Types.SMALLINT: case Types.INTEGER: - newprop.setIntegerValue (val.asLong()); + newprop.setIntegerValue (rs.getLong(rel.getDbField())); break; case Types.REAL: case Types.FLOAT: case Types.DOUBLE: - newprop.setFloatValue (val.asDouble()); + newprop.setFloatValue (rs.getDouble(rel.getDbField())); break; case Types.DECIMAL: case Types.NUMERIC: - java.math.BigDecimal num = val.asBigDecimal (); + java.math.BigDecimal num = rs.getBigDecimal (rel.getDbField()); if (num.scale() > 0) - newprop.setFloatValue (val.asDouble()); + newprop.setFloatValue (rs.getDouble(rel.getDbField())); else - newprop.setIntegerValue (val.asLong()); + newprop.setIntegerValue (rs.getLong(rel.getDbField())); break; case Types.LONGVARBINARY: case Types.VARBINARY: case Types.BINARY: - newprop.setStringValue (val.asString()); + newprop.setStringValue (rs.getString(rel.getDbField())); break; case Types.LONGVARCHAR: case Types.CHAR: case Types.VARCHAR: case Types.OTHER: - newprop.setStringValue (val.asString()); + newprop.setStringValue (rs.getString(rel.getDbField())); break; case Types.DATE: case Types.TIME: case Types.TIMESTAMP: - newprop.setDateValue (val.asTimestamp()); + newprop.setDateValue (rs.getTimestamp(rel.getDbField())); break; case Types.NULL: @@ -338,10 +338,13 @@ public final class Node implements INode, Serializable { // continue; default: - newprop.setStringValue (val.asString()); + newprop.setStringValue (rs.getString(rel.getDbField())); break; } + if (rs.wasNull()) + newprop.setStringValue (null); + if(propMap == null) propMap = new Hashtable (); propMap.put (rel.propName.toLowerCase(), newprop); diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index 380c5c60..c9a1a77a 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -601,26 +601,26 @@ public final class NodeManager { // Transactor tx = (Transactor) Thread.currentThread (); // tx.timer.beginEvent ("generateID "+map); - QueryDataSet qds = null; String retval = null; + Statement stmt = null; try { Connection con = map.getConnection (); String q = "SELECT MAX("+map.getIDField()+") FROM "+map.getTableName(); - qds = new QueryDataSet (con, q); - qds.fetchRecords (); + stmt = con.createStatement (); + ResultSet rs = stmt.executeQuery (q); // check for empty table - if (qds.size () == 0) { + if (!rs.next()) { long currMax = map.getNewID (0); retval = Long.toString (currMax); } else { - long currMax = qds.getRecord (0).getValue (1).asLong (); + long currMax = rs.getLong (1); currMax = map.getNewID (currMax); retval = Long.toString (currMax); } } finally { // tx.timer.endEvent ("generateID "+map); - if (qds != null) try { - qds.close (); + if (stmt != null) try { + stmt.close (); } catch (Exception ignore) {} } return retval; @@ -635,18 +635,18 @@ public final class NodeManager { // Transactor tx = (Transactor) Thread.currentThread (); // tx.timer.beginEvent ("generateID "+map); - QueryDataSet qds = null; + Statement stmt = null; String retval = null; try { Connection con = map.getConnection (); String q = "SELECT "+map.getIDgen()+".nextval FROM dual"; - qds = new QueryDataSet (con, q); - qds.fetchRecords (); - retval = qds.getRecord (0).getValue (1).asString (); + stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery (q); + retval = rs.getString (1); } finally { // tx.timer.endEvent ("generateID "+map); - if (qds != null) try { - qds.close (); + if (stmt != null) try { + stmt.close (); } catch (Exception ignore) {} } return retval; @@ -746,31 +746,31 @@ public final class NodeManager { List retval = new ArrayList (); DbMapping dbm = rel.otherType; - TableDataSet tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ()); + Connection con = dbm.getConnection (); + Statement stmt = con.createStatement (); + StringBuffer q = dbm.getSelect (); try { - if (home.getSubnodeRelation() != null) { - // HACK: cut off the "where" part of manually set subnoderelation - tds.where (home.getSubnodeRelation().trim().substring(5)); + q.append (home.getSubnodeRelation()); } else { // let relation object build the query - tds.where (rel.buildQuery (home, home.getNonVirtualParent (), null, "", false)); + q.append (" where "); + q.append (rel.buildQuery (home, home.getNonVirtualParent (), null, "", false)); if (rel.getOrder () != null) - tds.order (rel.getOrder ()); + q.append (" "+rel.getOrder ()); } if (logSql) - app.logEvent ("### getNodes: "+tds.getSelectString()); + app.logEvent ("### getNodes: "+q.toString()); if (rel.maxSize > 0) - tds.fetchRecords (rel.maxSize); - else - tds.fetchRecords (); + stmt.setMaxRows (rel.maxSize); - for (int i=0; i 0) { - TableDataSet tds = new TableDataSet (dbm.getConnection (), - dbm.getSchema (), - dbm.getKeyDef ()); + Connection con = dbm.getConnection (); + Statement stmt = con.createStatement (); + StringBuffer q = dbm.getSelect (); try { String idfield = rel.groupby != null ? rel.groupby : dbm.getIDField (); boolean needsQuotes = dbm.needsQuotes (idfield); - StringBuffer whereBuffer = new StringBuffer (idfield); - whereBuffer.append (" in ("); + q.append (" where "); + q.append (idfield); + q.append (" in ("); boolean first = true; for (int i=0; i 0 ? Math.min (rel.maxSize, retval) : retval; @@ -985,27 +984,26 @@ public final class NodeManager { Connection con = rel.otherType.getConnection (); String table = rel.otherType.getTableName (); - QueryDataSet qds = null; + Statement stmt = null; try { String q = "SELECT "+namefield+" FROM "+table+" ORDER BY "+namefield; - qds = new QueryDataSet (con, q); + stmt = con.createStatement (); if (logSql) - app.logEvent ("### getPropertyNames: "+qds.getSelectString()); + app.logEvent ("### getPropertyNames: "+q); - qds.fetchRecords (); - for (int i=0; i 1) + if (!rs.isLast ()) throw new RuntimeException ("More than one value returned by query."); - Record rec = tds.getRecord (0); - node = new Node (dbm, rec, safe); + node = new Node (dbm, rs, safe); } finally { - if (tds != null) try { - tds.close (); + if (stmt != null) try { + stmt.close (); } catch (Exception ignore) {} } } @@ -1086,35 +1089,35 @@ public final class NodeManager { return node; } else { - TableDataSet tds = null; + Statement stmt = null; try { DbMapping dbm = rel.otherType; - tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ()); - + Connection con = dbm.getConnection (); + StringBuffer q = dbm.getSelect (); if (home.getSubnodeRelation () != null) { // combine our key with the constraints in the manually set subnode relation - StringBuffer where = new StringBuffer (); - where.append (rel.accessor); - where.append (" = '"); - where.append (escape(kstr)); - where.append ("' AND "); - where.append (home.getSubnodeRelation ().trim().substring(5).trim()); - tds.where (where.toString ()); - } else - tds.where (rel.buildQuery (home, home.getNonVirtualParent (), kstr, "", false)); - + q.append (home.getSubnodeRelation ()); + q.append (" and "); + q.append (rel.accessor); + q.append (" = '"); + q.append (escape(kstr)); + q.append ("'"); + } else { + q.append (" where "); + q.append (rel.buildQuery (home, home.getNonVirtualParent (), kstr, "", false)); + } if (logSql) - app.logEvent ("### getNodeByRelation: "+tds.getSelectString()); + app.logEvent ("### getNodeByRelation: "+q.toString()); - tds.fetchRecords (); + stmt = con.createStatement (); + ResultSet rs = stmt.executeQuery (q.toString()); - if (tds.size () == 0) + if (!rs.next ()) return null; - if (tds.size () > 1) + if (!rs.isLast ()) throw new RuntimeException ("More than one value returned by query."); - Record rec = tds.getRecord (0); - node = new Node (rel.otherType, rec, safe); + node = new Node (rel.otherType, rs, safe); // Check if node is already cached with primary Key. if (!rel.usesPrimaryKey()) { @@ -1126,8 +1129,8 @@ public final class NodeManager { } } finally { - if (tds != null) try { - tds.close (); + if (stmt != null) try { + stmt.close (); } catch (Exception ignore) {} } } diff --git a/src/helma/objectmodel/db/Relation.java b/src/helma/objectmodel/db/Relation.java index c34d162b..035401fa 100644 --- a/src/helma/objectmodel/db/Relation.java +++ b/src/helma/objectmodel/db/Relation.java @@ -35,6 +35,9 @@ public final class Relation { // the DbMapping of the prototype we link to, unless this is a "primitive" (non-object) relation DbMapping otherType; + // the column type, as defined in java.sql.Types + int columnType; + // if this relation defines a virtual node, we need to provide a DbMapping for these virtual nodes DbMapping virtualMapping; @@ -240,6 +243,14 @@ public final class Relation { return propName; } + public void setColumnType (int ct) { + columnType = ct; + } + + public int getColumnType () { + return columnType; + } + /** * Add a constraint to the current list of constraints