Finished most of the basic implementation. Now on to magical table creation.

This commit is contained in:
Michael Bayne
2006-09-19 20:29:58 +00:00
parent 7727c5cfde
commit 4c89cdea33
2 changed files with 313 additions and 83 deletions
@@ -88,22 +88,6 @@ public class DepotMarshaller<T>
return _tableName;
}
/**
* Returns the field/column name of the primary key for this persistent
* object class.
*
* @exception UnsupportedOperationException thrown if this persistent
* object class did not define a primary key.
*/
public String getPrimaryKeyColumn ()
{
if (_primaryKey == null) {
throw new UnsupportedOperationException(
getClass().getName() + " does not define a primary key");
}
return _primaryKey.getColumnName();
}
/**
* Returns a key configured with the primary key of the supplied object.
* Throws an exception if the persistent object did not declare a primary
@@ -123,6 +107,15 @@ public class DepotMarshaller<T>
}
}
/**
* Creates a primary key record for the type of object handled by this
* marshaller, using the supplied primary key vlaue.
*/
public DepotRepository.Key makePrimaryKey (Comparable value)
{
return new DepotRepository.Key(_primaryKey.getColumnName(), value);
}
/**
* Creates a query for instances of this persistent object type using the
* supplied key.
@@ -134,9 +127,7 @@ public class DepotMarshaller<T>
String query = "select " + _fullColumnList + " from " + getTableName() +
" where " + key.toWhereClause();
PreparedStatement pstmt = conn.prepareStatement(query);
for (int ii = 0; ii < key.indices.length; ii++) {
pstmt.setObject(ii+1, key.values[ii]);
}
key.bindArguments(pstmt, 1);
return pstmt;
}
@@ -238,9 +229,12 @@ public class DepotMarshaller<T>
try {
PreparedStatement pstmt = conn.prepareStatement(update.toString());
idx = 0;
// bind the update arguments
for (String field : modifiedFields) {
_fields.get(field).setValue(po, pstmt, ++idx);
}
// now bind the key arguments
key.bindArguments(pstmt, ++idx);
return pstmt;
} catch (SQLException sqe) {
@@ -274,24 +268,56 @@ public class DepotMarshaller<T>
}
update.append(" where ").append(key.toWhereClause());
try {
PreparedStatement pstmt = conn.prepareStatement(update.toString());
idx = 0;
for (Object value : modifiedValues) {
// TODO: use the field marshaller?
pstmt.setObject(++idx, value);
}
return pstmt;
} catch (SQLException sqe) {
// pass this on through
throw sqe;
} catch (Exception e) {
String errmsg = "Failed to marshall persistent object " +
"[pclass=" + _pclass.getName() + "]";
throw (SQLException)new SQLException(errmsg).initCause(e);
PreparedStatement pstmt = conn.prepareStatement(update.toString());
idx = 0;
// bind the update arguments
for (Object value : modifiedValues) {
// TODO: use the field marshaller?
pstmt.setObject(++idx, value);
}
// now bind the key arguments
key.bindArguments(pstmt, ++idx);
return pstmt;
}
/**
* Creates a statement that will update the specified set of fields, using
* the supplied literal SQL values, for all persistent objects that match
* the supplied key.
*/
public PreparedStatement createLiteralUpdate (
Connection conn, DepotRepository.Key key,
String[] modifiedFields, Object[] modifiedValues)
throws SQLException
{
StringBuilder update = new StringBuilder();
update.append("update ").append(getTableName()).append(" set ");
for (int ii = 0; ii < modifiedFields.length; ii++) {
if (ii > 0) {
update.append(", ");
}
update.append(modifiedFields[ii]).append(" = ");
update.append(modifiedValues[ii]);
}
update.append(" where ").append(key.toWhereClause());
PreparedStatement pstmt = conn.prepareStatement(update.toString());
key.bindArguments(pstmt, 1);
return pstmt;
}
/**
* Creates a statement that will delete all rows matching the supplied key.
*/
public PreparedStatement createDelete (
Connection conn, DepotRepository.Key key)
throws SQLException
{
String query = "delete from " + getTableName() +
" where " + key.toWhereClause();
PreparedStatement pstmt = conn.prepareStatement(query);
key.bindArguments(pstmt, 1);
return pstmt;
}
protected Class<T> _pclass;
@@ -42,13 +42,18 @@ public class DepotRepository
_conprov = conprov;
}
/**
* Loads the persistent object that matches the specified primary key.
*/
protected <T> T load (Class<T> type, Comparable primaryKey)
throws PersistenceException
{
DepotMarshaller<T> marsh = getMarshaller(type);
return load(type, new Key(marsh.getPrimaryKeyColumn(), primaryKey));
return load(type, getMarshaller(type).makePrimaryKey(primaryKey));
}
/**
* Loads the first persistent object that matches the supplied key.
*/
protected <T> T load (Class<T> type, Key key)
throws PersistenceException
{
@@ -75,6 +80,9 @@ public class DepotRepository
});
}
/**
* Loads all persistent objects that match the specified key.
*/
protected <T,C extends Collection<T>> Collection<T> findAll (
Class<T> type, Key key)
throws PersistenceException
@@ -100,6 +108,10 @@ public class DepotRepository
});
}
/**
* Executes and caches the supplied collection query if the supplied key is
* not in the cache, returns the existing cached values otherwise.
*/
protected <T extends Collection> T findAll (
String index, Comparable key, CollectionQuery<T> query)
throws PersistenceException
@@ -107,17 +119,26 @@ public class DepotRepository
return null;
}
protected void insert (final Object record)
/**
* Inserts the supplied persistent object into the database, assigning its
* primary key (if it has one) in the process.
*
* @return the number of rows modified by this action, this should always
* be one.
*/
protected int insert (final Object record)
throws PersistenceException
{
final DepotMarshaller marsh = getMarshaller(record.getClass());
invoke(new Modifier(marsh.getPrimaryKey(record)) {
return invoke(new Modifier(marsh.getPrimaryKey(record)) {
public int invoke (Connection conn)
throws SQLException
{
PreparedStatement stmt = marsh.createInsert(conn, record);
try {
return stmt.executeUpdate();
int mods = stmt.executeUpdate();
// TODO: assign primary key
return mods;
} finally {
stmt.close();
}
@@ -125,16 +146,21 @@ public class DepotRepository
});
}
/**
* Updates all fields of the supplied persistent object, using its primary
* key to identify the row to be updated.
*
* @return the number of rows modified by this action.
*/
protected int update (final Object record)
throws PersistenceException
{
final DepotMarshaller marsh = getMarshaller(record.getClass());
invoke(new Modifier(marsh.getPrimaryKey(record)) {
return invoke(new Modifier(marsh.getPrimaryKey(record)) {
public int invoke (Connection conn)
throws SQLException
{
PreparedStatement stmt = marsh.createUpdate(
conn, record, marsh.getPrimaryKey(record));
PreparedStatement stmt = marsh.createUpdate(conn, record, _key);
try {
return stmt.executeUpdate();
} finally {
@@ -142,19 +168,24 @@ public class DepotRepository
}
}
});
return 0;
}
/**
* Updates just the specified fields of the supplied persistent object,
* using its primary key to identify the row to be updated.
*
* @return the number of rows modified by this action.
*/
protected int update (final Object record, final String ... modifiedFields)
throws PersistenceException
{
final DepotMarshaller marsh = getMarshaller(record.getClass());
invoke(new Modifier(marsh.getPrimaryKey(record)) {
return invoke(new Modifier(marsh.getPrimaryKey(record)) {
public int invoke (Connection conn)
throws SQLException
{
PreparedStatement stmt = marsh.createUpdate(
conn, record, marsh.getPrimaryKey(record), modifiedFields);
conn, record, _key, modifiedFields);
try {
return stmt.executeUpdate();
} finally {
@@ -162,54 +193,231 @@ public class DepotRepository
}
}
});
return 0;
}
protected <T> int updatePartial (Class<T> type,
Comparable primaryKey, Object ... fieldsValues)
/**
* Updates the specified columns for all persistent objects matching the
* supplied primary key.
*
* @param type the type of the persistent object to be modified.
* @param primaryKey the primary key to match in the update.
* @param fieldsValues an array containing the names of the fields/columns
* and the values to be assigned, in key, value, key, value, etc. order.
*
* @return the number of rows modified by this action.
*/
protected <T> int updatePartial (
Class<T> type, Comparable primaryKey, Object ... fieldsValues)
throws PersistenceException
{
return 0;
return updatePartial(
type, getMarshaller(type).makePrimaryKey(primaryKey), fieldsValues);
}
protected <T> int updatePartial (Class<T> type,
Key key, Object ... fieldsValues)
/**
* Updates the specified columns for all persistent objects matching the
* supplied key.
*
* @param type the type of the persistent object to be modified.
* @param key the key to match in the update.
* @param fieldsValues an array containing the names of the fields/columns
* and the values to be assigned, in key, value, key, value, etc. order.
*
* @return the number of rows modified by this action.
*/
protected <T> int updatePartial (
Class<T> type, Key key, Object ... fieldsValues)
throws PersistenceException
{
return 0;
// separate the arguments into keys and values
final String[] fields = new String[fieldsValues.length/2];
final Object[] values = new Object[fields.length];
for (int ii = 0, idx = 0; ii < fields.length; ii++) {
fields[ii] = (String)fieldsValues[idx++];
values[ii] = fieldsValues[idx++];
}
final DepotMarshaller marsh = getMarshaller(type);
return invoke(new Modifier(key) {
public int invoke (Connection conn)
throws SQLException
{
PreparedStatement stmt = marsh.createPartialUpdate(
conn, _key, fields, values);
try {
return stmt.executeUpdate();
} finally {
stmt.close();
}
}
});
}
protected <T> int updateLiteral (Class<T> type,
Comparable primaryKey, Object ... fieldsValues)
{
return 0;
}
protected <T> int updateLiteral (Class<T> type,
Key key, Object ... fieldsValues)
{
return 0;
}
protected int store (Object record)
/**
* Updates the specified columns for all persistent objects matching the
* supplied primary key. The values in this case must be literal SQL to be
* inserted into the update statement. In general this is used when you
* want to do something like the following:
*
* <pre>
* update FOO set BAR = BAR + 1;
* update BAZ set BIF = NOW();
* </pre>
*
* @param type the type of the persistent object to be modified.
* @param primaryKey the key to match in the update.
* @param fieldsValues an array containing the names of the fields/columns
* and the values to be assigned, in key, literal value, key, literal
* value, etc. order.
*
* @return the number of rows modified by this action.
*/
protected <T> int updateLiteral (
Class<T> type, Comparable primaryKey, String ... fieldsValues)
throws PersistenceException
{
return 0;
return updateLiteral(
type, getMarshaller(type).makePrimaryKey(primaryKey), fieldsValues);
}
protected void delete (Object record)
/**
* Updates the specified columns for all persistent objects matching the
* supplied primary key. The values in this case must be literal SQL to be
* inserted into the update statement. In general this is used when you
* want to do something like the following:
*
* <pre>
* update FOO set BAR = BAR + 1;
* update BAZ set BIF = NOW();
* </pre>
*
* @param type the type of the persistent object to be modified.
* @param key the key to match in the update.
* @param fieldsValues an array containing the names of the fields/columns
* and the values to be assigned, in key, literal value, key, literal
* value, etc. order.
*
* @return the number of rows modified by this action.
*/
protected <T> int updateLiteral (
Class<T> type, Key key, String ... fieldsValues)
throws PersistenceException
{
// separate the arguments into keys and values
final String[] fields = new String[fieldsValues.length/2];
final String[] values = new String[fields.length];
for (int ii = 0, idx = 0; ii < fields.length; ii++) {
fields[ii] = fieldsValues[idx++];
values[ii] = fieldsValues[idx++];
}
final DepotMarshaller marsh = getMarshaller(type);
return invoke(new Modifier(key) {
public int invoke (Connection conn)
throws SQLException
{
PreparedStatement stmt = marsh.createLiteralUpdate(
conn, _key, fields, values);
try {
return stmt.executeUpdate();
} finally {
stmt.close();
}
}
});
}
protected <T> void delete (Class<T> type, Comparable primaryKey)
/**
* Stores the supplied persisent object in the database. If it has no
* primary key assigned (it is null or zero), it will be inserted
* directly. Otherwise an update will first be attempted and if that
* matches zero rows, the object will be inserted.
*
* @return the number of rows modified by this action, this should always
* be one.
*/
protected int store (final Object record)
throws PersistenceException
{
final DepotMarshaller marsh = getMarshaller(record.getClass());
return invoke(new Modifier(marsh.getPrimaryKey(record)) {
public int invoke (Connection conn)
throws SQLException
{
PreparedStatement stmt = null;
try {
// if our primary key is null or is the integer 0, assume
// the record has never before been persisted and insert
if (_key != null && !Integer.valueOf(0).equals(_key)) {
stmt = marsh.createUpdate(conn, record, _key);
int mods = stmt.executeUpdate();
if (mods > 0) {
return mods;
}
stmt.close();
}
// if the update modified zero rows or the primary key was
// obviously unset, do an insertion
stmt = marsh.createInsert(conn, record);
return stmt.executeUpdate();
} finally {
stmt.close();
}
}
});
}
protected <T> void deleteAll (Class<T> type, Key key)
/**
* Deletes all persistent objects from the database with a primary key
* matching the primary key of the supplied object.
*
* @return the number of rows deleted by this action.
*/
protected <T> int delete (T record)
throws PersistenceException
{
@SuppressWarnings("unchecked") Class<T> type =
(Class<T>)record.getClass();
DepotMarshaller<T> marsh = getMarshaller(type);
return deleteAll(type, marsh.getPrimaryKey(record));
}
/**
* Deletes all persistent objects from the database with a primary key
* matching the supplied primary key.
*
* @return the number of rows deleted by this action.
*/
protected <T> int delete (Class<T> type, Comparable primaryKey)
throws PersistenceException
{
return deleteAll(type, getMarshaller(type).makePrimaryKey(primaryKey));
}
/**
* Deletes all persistent objects from the database that match the supplied
* key.
*
* @return the number of rows deleted by this action.
*/
protected <T> int deleteAll (Class<T> type, Key key)
throws PersistenceException
{
final DepotMarshaller marsh = getMarshaller(type);
return invoke(new Modifier(key) {
public int invoke (Connection conn)
throws SQLException
{
PreparedStatement stmt = marsh.createDelete(conn, _key);
try {
return stmt.executeUpdate();
} finally {
stmt.close();
}
}
});
}
protected <T> DepotMarshaller<T> getMarshaller (Class<T> type)
@@ -295,6 +503,14 @@ public class DepotRepository
}
return where.toString();
}
public void bindArguments (PreparedStatement stmt, int startIdx)
throws SQLException
{
for (int ii = 0; ii < indices.length; ii++) {
stmt.setObject(startIdx++, values[ii]);
}
}
}
protected static class Key2 extends Key
@@ -349,18 +565,6 @@ public class DepotRepository
protected Key _key;
}
// protected static abstract class InstanceUpdate<T>
// {
// public void invoke (T object)
// throws PersistenceException;
// }
// protected static abstract class CollectionUpdate<T>
// {
// public void invoke (Collection<T> collection)
// throws PersistenceException;
// }
protected ConnectionProvider _conprov;
protected HashMap<Class<?>, DepotMarshaller<?>> _marshallers =