From f2bfe5b9cf3a68d9767d085793b8cfb80c2f8479 Mon Sep 17 00:00:00 2001 From: hns Date: Thu, 5 Dec 2002 16:51:17 +0000 Subject: [PATCH] Added new DbColumn class that encapsulates column name, SQL type and the relation associated with the column. This helps us fix a bug where no column info is available because a column has no relation associated with it. It also helps us streamline the Relation lookup when creating a Node from a ResultSet. Use StringBuffers instead of + for query string composition where possible. --- src/helma/objectmodel/db/DbColumn.java | 46 ++++++++++++ src/helma/objectmodel/db/DbMapping.java | 83 +++++++++++++-------- src/helma/objectmodel/db/Node.java | 22 +++--- src/helma/objectmodel/db/NodeManager.java | 88 +++++++++++++++++------ 4 files changed, 176 insertions(+), 63 deletions(-) create mode 100644 src/helma/objectmodel/db/DbColumn.java diff --git a/src/helma/objectmodel/db/DbColumn.java b/src/helma/objectmodel/db/DbColumn.java new file mode 100644 index 00000000..394702dc --- /dev/null +++ b/src/helma/objectmodel/db/DbColumn.java @@ -0,0 +1,46 @@ +// DbColumn.java +// Copyright 2002 Hannes Wallnoefer, Helma.org + +package helma.objectmodel.db; + +/** + * A class that encapsulates the Column name and data type of a + * column in a relational table. + */ +public final class DbColumn { + + private final String name; + private final int type; + private final Relation relation; + + /** + * Constructor + */ + public DbColumn (String name, int type, Relation rel) { + this.name = name; + this.type = type; + this.relation = rel; + } + + /** + * Get the column name. + */ + public String getName() { + return name; + } + + /** + * Get this columns SQL data type. + */ + public int getType() { + return type; + } + + /** + * Return the relation associated with this column. May be null. + */ + public Relation getRelation() { + return relation; + } + +} \ No newline at end of file diff --git a/src/helma/objectmodel/db/DbMapping.java b/src/helma/objectmodel/db/DbMapping.java index d2186c25..ff169db9 100644 --- a/src/helma/objectmodel/db/DbMapping.java +++ b/src/helma/objectmodel/db/DbMapping.java @@ -13,8 +13,8 @@ import java.util.StringTokenizer; import java.sql.*; import com.workingdogs.village.*; -/** - * A DbMapping describes how a certain type of Nodes is to mapped to a +/** + * A DbMapping describes how a certain type of Nodes is to mapped to a * relational database table. Basically it consists of a set of JavaScript property-to- * Database row bindings which are represented by instances of the Relation class. */ @@ -43,15 +43,21 @@ public final class DbMapping implements Updatable { Relation subRelation; Relation propRelation; - // if this defines a subnode mapping with groupby layer, we need a DbMapping for those groupby nodes + // if this defines a subnode mapping with groupby layer, + // we need a DbMapping for those groupby nodes DbMapping groupbyMapping; // Map of property names to Relations objects HashMap prop2db; - // Map of db columns to Relations objects + // Map of db columns to Relations objects. + // Case insensitive, keys are stored in upper case so + // lookups must do a toUpperCase(). HashMap db2prop; + // list of columns to fetch from db - String[] columns = null; + DbColumn[] columns = null; + // Map of db columns by name + HashMap columnMap; // pre-rendered select statement String select = null; @@ -115,6 +121,8 @@ public final class DbMapping implements Updatable { prop2db = new HashMap (); db2prop = new HashMap (); + + columnMap = new HashMap (); parent = null; @@ -185,6 +193,7 @@ public final class DbMapping implements Updatable { keydef = null; // same with columns and select string columns = null; + columnMap.clear(); select = null; @@ -211,8 +220,8 @@ public final class DbMapping implements Updatable { rel.update (dbField, props); p2d.put (propName, rel); if (rel.columnName != null && - (rel.reftype == Relation.PRIMITIVE || - rel.reftype == Relation.REFERENCE)) + (rel.reftype == Relation.PRIMITIVE || + rel.reftype == Relation.REFERENCE)) d2p.put (rel.columnName.toUpperCase (), rel); // app.logEvent ("Mapping "+propName+" -> "+dbField); } @@ -231,7 +240,7 @@ public final class DbMapping implements Updatable { if (subRelation == null) subRelation = new Relation (subnodeMapping, "_children", this, props); subRelation.update (subnodeMapping, props); - // if subnodes are accessed via access name or group name, + // if subnodes are accessed via access name or group name, // the subnode relation is also the property relation. if (subRelation.accessor != null || subRelation.groupby != null) propRelation = subRelation; @@ -250,7 +259,7 @@ public final class DbMapping implements Updatable { } - /** + /** * Method in interface Updatable. */ public void remove () { @@ -479,7 +488,7 @@ public final class DbMapping implements Updatable { groupbyMapping.typename = subRelation.groupbyPrototype; } - + public void setPropertyRelation (Relation rel) { propRelation = rel; } @@ -567,11 +576,11 @@ public final class DbMapping implements Updatable { /** - * Return a Village Schema object for this DbMapping. + * Return an array of DbColumns for the relational table mapped by this DbMapping. */ - public synchronized String[] getColumns() throws ClassNotFoundException, SQLException { + public synchronized DbColumn[] getColumns() throws ClassNotFoundException, SQLException { if (!isRelational ()) - throw new SQLException ("Can't get Schema for non-relational data mapping"); + throw new SQLException ("Can't get columns for non-relational data mapping "+this); if (source == null && parentMapping != null) return parentMapping.getColumns (); // Use local variable cols to avoid synchronization (schema may be nulled elsewhere) @@ -580,25 +589,46 @@ public final class DbMapping implements Updatable { // and build a string of column names. Connection con = getConnection (); Statement stmt = con.createStatement (); - ResultSet rs = stmt.executeQuery ("select * from "+getTableName()+" where 1 = 0"); + String t = getTableName(); + if (t == null) + throw new SQLException ("Table name is null in getColumns() for "+this); + ResultSet rs = stmt.executeQuery ( + new StringBuffer("SELECT * FROM ") + .append(t).append(" WHERE 1 = 0").toString()); if (rs == null) - throw new SQLException ("Error retrieving DB scheme for "+this); + throw new SQLException ("Error retrieving columns for "+this); ResultSetMetaData meta = rs.getMetaData (); // ok, we have the meta data, now loop through mapping... int ncols = meta.getColumnCount (); - columns = new String[ncols]; + columns = new DbColumn[ncols]; for (int i=0; i 0) @@ -305,20 +305,20 @@ public final class Node implements INode, Serializable { case Types.LONGVARBINARY: case Types.VARBINARY: case Types.BINARY: - newprop.setStringValue (rs.getString(columns[i])); + newprop.setStringValue (rs.getString(columns[i].getName())); break; case Types.LONGVARCHAR: case Types.CHAR: case Types.VARCHAR: case Types.OTHER: - newprop.setStringValue (rs.getString(columns[i])); + newprop.setStringValue (rs.getString(columns[i].getName())); break; case Types.DATE: case Types.TIME: case Types.TIMESTAMP: - newprop.setDateValue (rs.getTimestamp(columns[i])); + newprop.setDateValue (rs.getTimestamp(columns[i].getName())); break; case Types.NULL: @@ -327,7 +327,7 @@ public final class Node implements INode, Serializable { // continue; default: - newprop.setStringValue (rs.getString(columns[i])); + newprop.setStringValue (rs.getString(columns[i].getName())); break; } diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index 0850372f..bab0a103 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -580,7 +580,13 @@ public final class NodeManager { try { Connection con = dbm.getConnection (); st = con.createStatement (); - st.executeUpdate ("DELETE FROM "+dbm.getTableName ()+" WHERE "+dbm.getIDField ()+" = "+node.getID ()); + st.executeUpdate (new StringBuffer ("DELETE FROM ") + .append(dbm.getTableName ()) + .append(" WHERE ") + .append(dbm.getIDField()) + .append(" = ") + .append(node.getID()) + .toString()); } finally { if (st != null) try { st.close (); @@ -605,7 +611,11 @@ public final class NodeManager { Statement stmt = null; try { Connection con = map.getConnection (); - String q = "SELECT MAX("+map.getIDField()+") FROM "+map.getTableName(); + String q = new StringBuffer("SELECT MAX(") + .append(map.getIDField()) + .append(") FROM ") + .append(map.getTableName()) + .toString(); stmt = con.createStatement (); ResultSet rs = stmt.executeQuery (q); // check for empty table @@ -639,7 +649,10 @@ public final class NodeManager { String retval = null; try { Connection con = map.getConnection (); - String q = "SELECT "+map.getIDgen()+".nextval FROM dual"; + String q = new StringBuffer("SELECT ") + .append(map.getIDgen()) + .append(".nextval FROM dual") + .toString(); stmt = con.createStatement(); ResultSet rs = stmt.executeQuery (q); if (!rs.next ()) @@ -677,15 +690,28 @@ public final class NodeManager { Statement stmt = null; try { - + String q = null; - + if (home.getSubnodeRelation() != null) { // subnode relation was explicitly set - q = "SELECT "+idfield+" FROM "+table+" "+home.getSubnodeRelation(); + q = new StringBuffer("SELECT ") + .append(idfield) + .append(" FROM ") + .append(table) + .append(" ") + .append(home.getSubnodeRelation()) + .toString(); } else { // let relation object build the query - q = "SELECT "+idfield+" FROM "+table + rel.buildQuery (home, home.getNonVirtualParent (), null, " WHERE ", true); + q = new StringBuffer("SELECT ") + .append(idfield) + .append(" FROM ") + .append(table) + .append(rel.buildQuery (home, + home.getNonVirtualParent (), null, + " WHERE ", true)) + .toString(); } if (logSql) @@ -695,7 +721,7 @@ public final class NodeManager { if (rel.maxSize > 0) stmt.setMaxRows (rel.maxSize); ResultSet result = stmt.executeQuery (q); - + // problem: how do we derive a SyntheticKey from a not-yet-persistent Node? Key k = rel.groupby != null ? home.getKey (): null; while (result.next ()) { @@ -706,8 +732,8 @@ public final class NodeManager { continue; // make the proper key for the object, either a generic DB key or a groupby key Key key = rel.groupby == null ? - (Key) new DbKey (rel.otherType, kstr) : - (Key) new SyntheticKey (k, kstr); + (Key) new DbKey (rel.otherType, kstr) : + (Key) new SyntheticKey (k, kstr); retval.add (new NodeHandle (key)); // if these are groupby nodes, evict nullNode keys if (rel.groupby != null) { @@ -750,7 +776,7 @@ public final class NodeManager { Connection con = dbm.getConnection (); Statement stmt = con.createStatement (); - String[] columns = dbm.getColumns (); + DbColumn[] columns = dbm.getColumns (); StringBuffer q = dbm.getSelect (); try { if (home.getSubnodeRelation() != null) { @@ -808,7 +834,7 @@ public final class NodeManager { if (missing > 0) { Connection con = dbm.getConnection (); Statement stmt = con.createStatement (); - String[] columns = dbm.getColumns (); + DbColumn[] columns = dbm.getColumns (); StringBuffer q = dbm.getSelect (); try { String idfield = rel.groupby != null ? rel.groupby : dbm.getIDField (); @@ -835,8 +861,10 @@ public final class NodeManager { q.append (") "); if (rel.groupby != null) { q.append (rel.renderConstraints (home, home.getNonVirtualParent ())); - if (rel.order != null) - q.append (" ORDER BY "+rel.order); + if (rel.order != null) { + q.append (" ORDER BY "); + q.append (rel.order); + } } if (logSql) @@ -937,19 +965,27 @@ public final class NodeManager { Statement stmt = null; try { - + String q = null; if (home.getSubnodeRelation() != null) { // use the manually set subnoderelation of the home node - q = "SELECT count(*) FROM "+table+" "+home.getSubnodeRelation(); + q = new StringBuffer("SELECT count(*) FROM ") + .append(table) + .append(" ") + .append(home.getSubnodeRelation()) + .toString(); } else { // let relation object build the query - q = "SELECT count(*) FROM "+table + rel.buildQuery (home, home.getNonVirtualParent (), null, " WHERE ", false); + q = new StringBuffer("SELECT count(*) FROM ") + .append(table) + .append(rel.buildQuery (home, home.getNonVirtualParent(), + null, " WHERE ", false)) + .toString(); } - + if (logSql) app.logEvent ("### countNodes: "+q); - + stmt = con.createStatement(); ResultSet rs = stmt.executeQuery (q); @@ -990,7 +1026,13 @@ public final class NodeManager { Statement stmt = null; try { - String q = "SELECT "+namefield+" FROM "+table+" ORDER BY "+namefield; + String q = new StringBuffer("SELECT ") + .append(namefield) + .append(" FROM ") + .append(table) + .append(" ORDER BY ") + .append(namefield) + .toString(); stmt = con.createStatement (); if (logSql) @@ -1037,7 +1079,7 @@ public final class NodeManager { Connection con = dbm.getConnection (); stmt = con.createStatement (); - String[] columns = dbm.getColumns (); + DbColumn[] columns = dbm.getColumns (); StringBuffer q = dbm.getSelect (); q.append ("WHERE "); q.append (idfield); @@ -1099,7 +1141,7 @@ public final class NodeManager { DbMapping dbm = rel.otherType; Connection con = dbm.getConnection (); - String[] columns = dbm.getColumns (); + DbColumn[] columns = dbm.getColumns (); StringBuffer q = dbm.getSelect (); if (home.getSubnodeRelation () != null) { // combine our key with the constraints in the manually set subnode relation @@ -1144,7 +1186,7 @@ public final class NodeManager { } /** - * 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. */ public DbMapping getDbMapping (String protoname) {