Make persistence layer independent from Village API.

This commit is contained in:
hns 2003-01-27 18:20:32 +00:00
parent e3061ffba2
commit df3b4de13f
5 changed files with 362 additions and 236 deletions

View file

@ -20,6 +20,8 @@ public final class DbColumn {
this.name = name;
this.type = type;
this.relation = rel;
if (relation != null)
relation.setColumnType (type);
}
/**

View file

@ -58,8 +58,11 @@ public final class DbMapping implements Updatable {
DbColumn[] columns = null;
// Map of db columns by name
HashMap columnMap;
// pre-rendered select statement
String select = null;
String selectString = null;
String insertString = null;
String updateString = null;
// db field used as primary key
private String idField;
@ -194,7 +197,7 @@ public final class DbMapping implements Updatable {
// same with columns and select string
columns = null;
columnMap.clear();
select = null;
selectString = insertString = updateString = null;
if (extendsProto != null) {
@ -634,14 +637,40 @@ public final class DbMapping implements Updatable {
}
public StringBuffer getSelect () throws SQLException, ClassNotFoundException {
String sel = select;
String sel = selectString;
if (sel != null)
return new StringBuffer (sel);
StringBuffer s = new StringBuffer ("SELECT * FROM ");
s.append (getTableName ());
s.append (" ");
// cache rendered string for later calls.
select = s.toString();
selectString = s.toString();
return s;
}
public StringBuffer getInsert () {
String ins = insertString;
if (ins != null)
return new StringBuffer (ins);
StringBuffer s = new StringBuffer ("INSERT INTO ");
s.append (getTableName ());
s.append (" ( ");
s.append (getIDField());
// cache rendered string for later calls.
insertString = s.toString();
return s;
}
public StringBuffer getUpdate () {
String upd = updateString;
if (upd != null)
return new StringBuffer (upd);
StringBuffer s = new StringBuffer ("UPDATE ");
s.append (getTableName ());
s.append (" SET ");
// cache rendered string for later calls.
updateString = s.toString();
return s;
}

View file

@ -1748,11 +1748,20 @@ public final class Node implements INode, Serializable {
if (propMap == null)
return;
try {
Property p = (Property) propMap.remove (propname.toLowerCase ());
// if node is relational, leave a null property so that it is
// updated in the DB. Otherwise, remove the property.
Property p;
boolean relational = dbmap != null && dbmap.isRelational();
if (relational)
p = (Property) propMap.get (propname.toLowerCase ());
else
p = (Property) propMap.remove (propname.toLowerCase ());
if (p != null) {
checkWriteLock ();
if (p.getType() == Property.NODE)
p.unregisterNode ();
if (relational)
p.setStringValue (null);
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
lastmodified = System.currentTimeMillis ();
if (state == CLEAN)

View file

@ -9,7 +9,6 @@ import helma.framework.core.Application;
import java.sql.*;
import java.io.*;
import java.util.*;
import com.workingdogs.village.*;
/**
* The NodeManager is responsible for fetching Nodes from the internal or
@ -410,62 +409,117 @@ public final class NodeManager {
db.saveNode (txn, node.getID (), node);
} else {
// app.logEvent ("inserting relational node: "+node.getID ());
TableDataSet tds = null;
DbColumn[] columns = dbm.getColumns ();
StringBuffer b1 = dbm.getInsert ();
StringBuffer b2 = new StringBuffer (" ) VALUES ( ?");
String nameField = dbm.getNameField ();
String prototypeField = dbm.getPrototypeField ();
for (int i=0; i<columns.length; i++) {
Relation rel = columns[i].getRelation();
String name = columns[i].getName();
if ((rel != null && (rel.isPrimitive() || rel.isReference())) ||
name.equals (nameField) || name.equals (prototypeField))
{
b1.append (", "+columns[i].getName());
b2.append (", ?");
System.err.println ("ADDING COLUMN: "+columns[i].getName());
}
}
b1.append (b2);
b1.append (" )");
Connection con = dbm.getConnection ();
PreparedStatement stmt = con.prepareStatement (b1.toString ());
try {
tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
Record rec = tds.addRecord ();
rec.setValue (dbm.getIDField (), node.getID ());
String nameField = dbm.getNameField ();
if (nameField != null)
rec.setValue (nameField, node.getName ());
int stmtNumber = 1;
stmt.setString (stmtNumber, node.getID());
for (Iterator i=dbm.getProp2DB().entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next ();
String propname = (String) e.getKey ();
Relation rel = (Relation) e.getValue ();
Property p = node.getProperty (propname);
Hashtable propMap = node.getPropMap ();
for (int i=0; i<columns.length; i++) {
Relation rel = columns[i].getRelation();
Property p = null;
if (rel != null && (rel.isPrimitive() || rel.isReference()))
p = (Property) propMap.get (rel.getPropName ());
String name = columns[i].getName ();
if (!(rel != null && (rel.isPrimitive() || rel.isReference())) && !name.equals (nameField) && !name.equals (prototypeField))
continue;
if (p != null && rel != null) {
switch (p.getType ()) {
case IProperty.STRING:
rec.setValue (rel.getDbField(), p.getStringValue ());
break;
case IProperty.BOOLEAN:
rec.setValue (rel.getDbField(), p.getBooleanValue ());
break;
case IProperty.DATE:
Timestamp t = new Timestamp (p.getDateValue ().getTime ());
rec.setValue (rel.getDbField(), t);
break;
case IProperty.INTEGER:
rec.setValue (rel.getDbField(), p.getIntegerValue ());
break;
case IProperty.FLOAT:
rec.setValue (rel.getDbField(), p.getFloatValue ());
break;
case IProperty.NODE:
if (rel.reftype == Relation.REFERENCE) {
// INode n = p.getNodeValue ();
// String foreignID = n == null ? null : n.getID ();
rec.setValue (rel.getDbField(), p.getStringValue ());
}
break;
stmtNumber++;
if (p != null) {
if (p.getValue() == null) {
stmt.setNull (stmtNumber, columns[i].getType ());
} else {
switch (columns[i].getType ()) {
case Types.BIT:
case Types.TINYINT:
case Types.BIGINT:
case Types.SMALLINT:
case Types.INTEGER:
stmt.setLong (stmtNumber, p.getIntegerValue());
break;
case Types.REAL:
case Types.FLOAT:
case Types.DOUBLE:
case Types.NUMERIC:
case Types.DECIMAL:
stmt.setDouble (stmtNumber, p.getFloatValue());
break;
case Types.LONGVARBINARY:
case Types.VARBINARY:
case Types.BINARY:
case Types.BLOB:
stmt.setString (stmtNumber, p.getStringValue());
break;
case Types.LONGVARCHAR:
case Types.CHAR:
case Types.VARCHAR:
case Types.OTHER:
stmt.setString (stmtNumber, p.getStringValue());
break;
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
stmt.setTimestamp (stmtNumber, p.getTimestampValue());
break;
case Types.NULL:
stmt.setNull (stmtNumber, 0);
break;
default:
stmt.setString (stmtNumber, p.getStringValue());
break;
}
}
p.dirty = false;
} else if (rel != null && rel.getDbField() != null) {
rec.setValueNull (rel.getDbField());
} else {
if (name.equals (nameField))
stmt.setString (stmtNumber, node.getName());
else if (name.equals (prototypeField))
stmt.setString (stmtNumber, node.getPrototype ());
else
stmt.setNull (stmtNumber, columns[i].getType());
}
}
stmt.executeUpdate ();
if (dbm.getPrototypeField () != null) {
rec.setValue (dbm.getPrototypeField (), node.getPrototype ());
}
rec.markForInsert ();
tds.save ();
} catch (Exception x) {
x.printStackTrace ();
throw x;
} finally {
if (tds != null) try {
tds.close ();
if (stmt != null) try {
stmt.close ();
} catch (Exception ignore) {}
}
dbm.notifyDataChange ();
@ -489,82 +543,134 @@ public final class NodeManager {
db.saveNode (txn, node.getID (), node);
} else {
TableDataSet tds = null;
Hashtable propMap = node.getPropMap ();
Property[] props = new Property[propMap.size()];
propMap.values().toArray (props);
// make sure table meta info is loaded by dbmapping
dbm.getColumns ();
StringBuffer b = dbm.getUpdate ();
boolean comma = false;
for (int i=0; i<props.length; i++) {
// skip clean properties
if (props[i] == null || !props[i].dirty) {
// null out clean property so we don't consider it later
props[i] = null;
continue;
}
Relation rel = dbm.propertyToRelation (props[i].getName());
// skip readonly, virtual and collection relations
if (rel == null || rel.readonly || rel.virtual ||
(rel.reftype != Relation.REFERENCE && rel.reftype != Relation.PRIMITIVE))
{
// null out property so we don't consider it later
props[i] = null;
continue;
}
if (comma)
b.append (", ");
else
comma = true;
b.append (rel.getDbField());
b.append (" = ?");
}
// if no columns were updated, return
if (!comma)
return;
b.append (" WHERE ");
b.append (dbm.getIDField ());
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 ();
PreparedStatement stmt = con.prepareStatement (b.toString ());
System.err.println (b.toString());
int stmtNumber = 0;
try {
tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ());
Record rec = tds.addRecord ();
rec.setValue (dbm.getIDField (), node.getID ());
int updated = 0;
for (Iterator i=dbm.getProp2DB().entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next ();
String propname = (String) e.getKey ();
Relation rel = (Relation) e.getValue ();
// skip properties that don't need to be updated before fetching them
if (rel != null && (rel.readonly || rel.virtual ||
(rel.reftype != Relation.REFERENCE && rel.reftype != Relation.PRIMITIVE)))
for (int i=0; i<props.length; i++) {
Property p = props[i];
if (p == null)
continue;
Relation rel = dbm.propertyToRelation (p.getName());
Property p = node.getProperty (propname);
stmtNumber++;
if (p != null && rel != null) {
if (p.getValue() == null) {
stmt.setNull (stmtNumber, rel.getColumnType ());
} else {
switch (rel.getColumnType ()) {
case Types.BIT:
case Types.TINYINT:
case Types.BIGINT:
case Types.SMALLINT:
case Types.INTEGER:
stmt.setLong (stmtNumber, p.getIntegerValue());
break;
if (p.dirty) {
switch (p.getType ()) {
case IProperty.STRING:
updated++;
rec.setValue (rel.getDbField(), p.getStringValue ());
break;
case IProperty.BOOLEAN:
updated++;
rec.setValue (rel.getDbField(), p.getBooleanValue ());
break;
case IProperty.DATE:
updated++;
Timestamp t = new Timestamp (p.getDateValue ().getTime ());
rec.setValue (rel.getDbField(), t);
break;
case IProperty.INTEGER:
updated++;
rec.setValue (rel.getDbField(), p.getIntegerValue ());
break;
case IProperty.FLOAT:
updated++;
rec.setValue (rel.getDbField(), p.getFloatValue ());
break;
case IProperty.NODE:
if (!rel.virtual && rel.reftype == Relation.REFERENCE) {
// INode n = p.getNodeValue ();
// String foreignID = n == null ? null : n.getID ();
updated++;
rec.setValue (rel.getDbField(), p.getStringValue ());
}
break;
}
p.dirty = false;
if (!rel.isPrivate())
markMappingAsUpdated = true;
case Types.REAL:
case Types.FLOAT:
case Types.DOUBLE:
case Types.NUMERIC:
case Types.DECIMAL:
stmt.setDouble (stmtNumber, p.getFloatValue());
break;
case Types.LONGVARBINARY:
case Types.VARBINARY:
case Types.BINARY:
case Types.BLOB:
stmt.setString (stmtNumber, p.getStringValue());
break;
case Types.LONGVARCHAR:
case Types.CHAR:
case Types.VARCHAR:
case Types.OTHER:
stmt.setString (stmtNumber, p.getStringValue());
break;
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
stmt.setTimestamp (stmtNumber, p.getTimestampValue());
break;
case Types.NULL:
stmt.setNull (stmtNumber, 0);
break;
default:
stmt.setString (stmtNumber, p.getStringValue());
break;
}
} else if (rel != null && rel.getDbField() != null) {
updated++;
rec.setValueNull (rel.getDbField());
}
p.dirty = false;
if (!rel.isPrivate())
markMappingAsUpdated = true;
}
if (updated > 0) {
// mark the key value as clean so no try is made to update it
rec.markValueClean (dbm.getIDField ());
rec.markForUpdate ();
tds.save ();
}
stmt.executeUpdate ();
} catch (Exception x) {
x.printStackTrace ();
throw x;
} finally {
if (tds != null) try {
tds.close ();
if (stmt != null) try {
stmt.close ();
} catch (Exception ignore) {}
}
if (markMappingAsUpdated)
dbm.notifyDataChange ();
}

View file

@ -8,6 +8,7 @@ import java.util.*;
import java.io.*;
import java.text.*;
import helma.objectmodel.*;
import java.sql.Timestamp;
/**
* A property implementation for Nodes stored inside a database. Basically
@ -19,13 +20,7 @@ public final class Property implements IProperty, Serializable, Cloneable {
private String propname;
private Node node;
private String svalue;
private boolean bvalue;
private long lvalue;
private double dvalue;
// protected String nvalueID;
private NodeHandle nhandle;
private Object jvalue;
private Object value;
private int type;
@ -42,29 +37,29 @@ public final class Property implements IProperty, Serializable, Cloneable {
case STRING:
// try to convert from old format
if (node.version < 7)
svalue = in.readUTF ();
value = in.readUTF ();
else
svalue = (String) in.readObject ();
value = in.readObject ();
break;
case BOOLEAN:
bvalue = in.readBoolean ();
value = in.readBoolean () ? Boolean.TRUE : Boolean.FALSE;
break;
case INTEGER:
case DATE:
lvalue = in.readLong ();
value = new Long (in.readLong ());
break;
case FLOAT:
dvalue = in.readDouble ();
value = new Double (in.readDouble ());
break;
case NODE:
// try to convert from old format
if (node.version > 4)
nhandle = (NodeHandle) in.readObject ();
value = (NodeHandle) in.readObject ();
else
nhandle = new NodeHandle (new DbKey (null, in.readUTF ()));
value = new NodeHandle (new DbKey (null, in.readUTF ()));
break;
case JAVAOBJECT:
jvalue = in.readObject ();
value = in.readObject ();
break;
}
} catch (ClassNotFoundException x) {
@ -78,26 +73,26 @@ public final class Property implements IProperty, Serializable, Cloneable {
out.writeInt (type);
switch (type) {
case STRING:
out.writeObject (svalue);
out.writeObject (value);
break;
case BOOLEAN:
out.writeBoolean (bvalue);
out.writeBoolean (((Boolean) value).booleanValue());
break;
case INTEGER:
case DATE:
out.writeLong (lvalue);
out.writeLong (((Long) value).longValue());
break;
case FLOAT:
out.writeDouble (dvalue);
out.writeDouble (((Double) value).doubleValue());
break;
case NODE:
out.writeObject (nhandle);
out.writeObject (value);
break;
case JAVAOBJECT:
if (jvalue != null && !(jvalue instanceof Serializable))
if (value != null && !(value instanceof Serializable))
out.writeObject (null);
else
out.writeObject (jvalue);
out.writeObject (value);
break;
}
}
@ -114,10 +109,10 @@ public final class Property implements IProperty, Serializable, Cloneable {
dirty = true;
}
public Property (String propname, Node node, Node value) {
public Property (String propname, Node node, Node valueNode) {
this (propname, node);
type = NODE;
nhandle = value == null ? null : value.getHandle ();
value = valueNode == null ? null : valueNode.getHandle ();
dirty = true;
}
@ -126,119 +121,92 @@ public final class Property implements IProperty, Serializable, Cloneable {
}
public Object getValue () {
switch (type) {
case STRING:
return svalue;
case BOOLEAN:
return new Boolean (bvalue);
case INTEGER:
return new Long (lvalue);
case FLOAT:
return new Double (dvalue);
case DATE:
return new Date (lvalue);
case NODE:
return null;
case JAVAOBJECT:
return jvalue;
}
return null;
return value;
}
public void setStringValue (String value) {
public int getType () {
return type;
}
public void setStringValue (String str) {
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
type = STRING;
this.svalue = value;
value = str;
dirty = true;
}
public void setIntegerValue (long value) {
public void setIntegerValue (long l) {
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
type = INTEGER;
this.lvalue = value;
value = new Long(l);
dirty = true;
}
public void setFloatValue (double value) {
public void setFloatValue (double d) {
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
type = FLOAT;
this.dvalue = value;
value = new Double(d);
dirty = true;
}
public void setDateValue (Date value) {
public void setDateValue (Date date) {
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
type = DATE;
this.lvalue = value == null ? 0 : value.getTime();
value = date;
dirty = true;
}
public void setBooleanValue (boolean value) {
public void setBooleanValue (boolean bool) {
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
type = BOOLEAN;
this.bvalue = value;
value = bool ? Boolean.TRUE : Boolean.FALSE;
dirty = true;
}
public void setNodeValue (Node value) {
public void setNodeValue (Node node) {
// value.checkWriteLock ();
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
// registerNode (value);
type = NODE;
nhandle = value.getHandle ();
value = node == null ? null : node.getHandle ();
dirty = true;
}
public void setNodeHandle (NodeHandle value) {
public void setNodeHandle (NodeHandle handle) {
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
// registerNode (value);
type = NODE;
nhandle = value;
value = handle;
dirty = true;
}
public NodeHandle getNodeHandle () {
return nhandle;
if (type == NODE)
return (NodeHandle) value;
return null;
}
public void convertToNodeReference (DbMapping dbm) {
String id = getStringValue ();
if (id == null)
nhandle = null;
else
nhandle = new NodeHandle (new DbKey (dbm, id));
if (value != null && !(value instanceof NodeHandle))
value = new NodeHandle (new DbKey (dbm, value.toString ()));
type = NODE;
}
public void setJavaObjectValue (Object value) {
public void setJavaObjectValue (Object obj) {
if (type == NODE)
unregisterNode ();
type = JAVAOBJECT;
this.jvalue = value;
value = obj;
}
@ -247,9 +215,10 @@ public final class Property implements IProperty, Serializable, Cloneable {
* If this was the "main" property for the node, also remove all other references.
*/
protected void unregisterNode () {
Node nvalue = null;
if (nhandle != null)
nvalue = nhandle.getNode (node.nmgr);
if (value == null || !(value instanceof NodeHandle))
return;
NodeHandle nhandle = (NodeHandle) value;
Node nvalue = nhandle.getNode (node.nmgr);
DbMapping nvmap = null;
Relation nvrel = null;
@ -281,22 +250,20 @@ public final class Property implements IProperty, Serializable, Cloneable {
public String getStringValue () {
if (value == null)
return null;
switch (type) {
case STRING:
return svalue;
case BOOLEAN:
return bvalue ? "true" : "false";
case DATE:
SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy HH:mm");
return format.format (new Date (lvalue));
case INTEGER:
return Long.toString (lvalue);
case FLOAT:
return Double.toString (dvalue);
case NODE:
return nhandle == null ? null : nhandle.getID ();
case JAVAOBJECT:
return jvalue == null ? null : jvalue.toString ();
return value.toString ();
case DATE:
SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm:ss");
return format.format ((Date) value);
case NODE:
return ((NodeHandle) value).getID ();
}
return "";
}
@ -307,50 +274,63 @@ public final class Property implements IProperty, Serializable, Cloneable {
public long getIntegerValue () {
if (type == INTEGER)
return lvalue;
return 0;
return ((Long) value).longValue ();
if (type == FLOAT)
return ((Double) value).longValue ();
try {
return Long.parseLong (getStringValue());
} catch (Exception x) {
return 0;
}
}
public double getFloatValue () {
if (type == FLOAT)
return dvalue;
return 0.0;
return ((Double) value).doubleValue();
if (type == INTEGER)
return ((Long) value).doubleValue ();
try {
return Double.parseDouble (getStringValue());
} catch (Exception x) {
return 0.0;
}
}
public Date getDateValue () {
if (type == DATE)
return new Date (lvalue);
return (Date) value;
return null;
}
public Timestamp getTimestampValue () {
if (type == DATE && value != null)
return new Timestamp (((Date) value).getTime());
return null;
}
public boolean getBooleanValue () {
if (type == BOOLEAN)
return bvalue;
return ((Boolean) value).booleanValue();
if (type == INTEGER)
return !(0 == getIntegerValue());
return false;
}
public INode getNodeValue () {
if (nhandle != null) {
Node n = nhandle.getNode (node.nmgr);
if (n != null) return n;
if (type == NODE && value != null) {
NodeHandle nhandle = (NodeHandle) value;
return nhandle.getNode (node.nmgr);
}
return null;
}
public Object getJavaObjectValue () {
if (type == JAVAOBJECT)
return jvalue;
return value;
return null;
}
public int getType () {
return type;
}
}