Some fixes and documentation from Zell.

This commit is contained in:
Michael Bayne
2007-01-14 20:40:54 +00:00
parent 8f4b37d9dc
commit 935bd34621
3 changed files with 82 additions and 24 deletions
@@ -251,22 +251,54 @@ public class DepotMarshaller<T>
}
/**
* Returns a key configured with the primary key of the supplied object. Throws an exception
* if the persistent object did not declare a primary key.
* Returns a key configured with the primary key of the supplied object. If all the fields are
* null, this method returns null. An exception is thrown if some of the fields are null and
* some are not, or if the object does not declare a primary key.
*/
public Key<T> getPrimaryKey (Object object)
{
return getPrimaryKey(object, true);
}
/**
* Returns a key configured with the primary key of the supplied object. If all the fields are
* null, this method returns null. If some of the fields are null and some are not, an
* exception is thrown. If the object does not declare a primary key and the second argument is
* true, this method throws an exception; if it's false, the method returns null.
*/
public Key<T> getPrimaryKey (Object object, boolean requireKey)
{
if (!hasPrimaryKey()) {
throw new UnsupportedOperationException(
_pclass.getName() + " does not define a primary key");
if (requireKey) {
throw new UnsupportedOperationException(
_pclass.getName() + " does not define a primary key");
}
return null;
}
try {
Comparable[] values = new Comparable[_pkColumns.size()];
boolean hasNulls = false;
for (int ii = 0; ii < _pkColumns.size(); ii++) {
FieldMarshaller field = _pkColumns.get(ii);
values[ii] = (Comparable) field.getField().get(object);
if (values[ii] == null || Integer.valueOf(0).equals(values[ii])) {
// if this is the first null we see but not the first field, freak out
if (!hasNulls && ii > 0) {
throw new IllegalArgumentException(
"Persistent object's primary key fields are mixed null and non-null.");
}
hasNulls = true;
} else if (hasNulls) {
// if this is a non-null field and we've previously seen nulls, also freak
throw new IllegalArgumentException(
"Persistent object's primary key fields are mixed null and non-null.");
}
}
return makePrimaryKey(values);
// if all the fields were null, return null, else build a key
return hasNulls ? null : makePrimaryKey(values);
} catch (IllegalAccessException iae) {
throw new RuntimeException(iae);
}
@@ -189,7 +189,9 @@ public class DepotRepository
throws PersistenceException
{
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
return _ctx.invoke(new CachingModifier<T>(null, null) {
final Key key = marsh.getPrimaryKey(record, false);
// key will be null if record was supplied without a primary key
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
public int invoke (Connection conn) throws SQLException {
// update our modifier's key so that it can cache our results
updateKey(marsh.assignPrimaryKey(conn, record, false));
@@ -198,7 +200,6 @@ public class DepotRepository
int mods = stmt.executeUpdate();
// check again in case we have a post-factum key generator
updateKey(marsh.assignPrimaryKey(conn, record, true));
setInstance(record);
return mods;
} finally {
JDBCUtil.close(stmt);
@@ -218,7 +219,10 @@ public class DepotRepository
{
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
final Key key = marsh.getPrimaryKey(record);
return _ctx.invoke(new CachingModifier<T>(key, key) {
if (key == null) {
throw new IllegalArgumentException("Can't update record with null primary key.");
}
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
public int invoke (Connection conn) throws SQLException {
PreparedStatement stmt = marsh.createUpdate(conn, record, key);
try {
@@ -241,7 +245,10 @@ public class DepotRepository
{
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
final Key key = marsh.getPrimaryKey(record);
return _ctx.invoke(new CachingModifier<T>(key, key) {
if (key == null) {
throw new IllegalArgumentException("Can't update record with null primary key.");
}
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
public int invoke (Connection conn) throws SQLException {
PreparedStatement stmt = marsh.createUpdate(conn, record, key, modifiedFields);
try {
@@ -408,13 +415,13 @@ public class DepotRepository
{
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
final Key key = marsh.hasPrimaryKey() ? marsh.getPrimaryKey(record) : null;
return _ctx.invoke(new CachingModifier<T>(key, key) {
return _ctx.invoke(new CachingModifier<T>(record, key, key) {
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
// if our primary key isn't null, update rather than insert the record
// before been persisted and insert
if (key != null && !Integer.valueOf(0).equals(key)) {
if (key != null) {
stmt = marsh.createUpdate(conn, record, key);
int mods = stmt.executeUpdate();
if (mods > 0) {
@@ -429,7 +436,6 @@ public class DepotRepository
stmt = marsh.createInsert(conn, record);
int mods = stmt.executeUpdate();
updateKey(marsh.assignPrimaryKey(conn, record, true));
setInstance(record);
return mods;
} finally {
@@ -450,6 +456,9 @@ public class DepotRepository
{
@SuppressWarnings("unchecked") Class<T> type = (Class<T>)record.getClass();
Key<T> primaryKey = _ctx.getMarshaller(type).getPrimaryKey(record);
if (primaryKey == null) {
throw new IllegalArgumentException("Can't delete record with null primary key.");
}
return deleteAll(type, primaryKey, primaryKey);
}
@@ -53,23 +53,29 @@ public abstract class Modifier
}
/**
* A simple modifier that updates the cache with its modified object on completion. The derived
* class must call {@link #setInstance} to inform the modifier of its object at some point
* during the modification operation.
* A convenience modifier that can perform cache updates in addition to invalidation:
* - Before {@link #invoke(Connection)}, the {@link CacheInvalidator} is run, if given.
* - After {@link #invoke(Connection)}, the cache is updated with the modified object,
* presuming both _key and _result are non-null. These variables may be set or modified
* during execution in addition to being supplied to the constructor.
*/
public static abstract class CachingModifier<T> extends Modifier
{
protected CachingModifier (CacheKey key, CacheInvalidator invalidator)
/**
* Construct a new CachingModifier with the given result, cache key, and invalidator,
* all of which are optional, and may also be set during execution.
*/
protected CachingModifier (T result, CacheKey key, CacheInvalidator invalidator)
{
super(invalidator);
_result = result;
_key = key;
}
protected void setInstance (T result)
{
_result = result;
}
/**
* Update this {@link CachingModifier}'s cache key, e.g. during insertion when a
* persistent object first receives a generated key.
*/
protected void updateKey (CacheKey key)
{
if (key != null) {
@@ -77,11 +83,12 @@ public abstract class Modifier
}
}
@Override
@Override // from Modifier
public void cacheUpdate (PersistenceContext ctx)
{
super.cacheUpdate(ctx);
if (_key != null) {
// if we have both a key and a record, cache
if (_key != null && _result != null) {
ctx.cacheStore(_key, _result);
}
}
@@ -90,13 +97,23 @@ public abstract class Modifier
protected T _result;
}
/**
* Overriden to perform the actual database modifications represented by this object;
* should return the number of modified rows.
*/
public abstract int invoke (Connection conn) throws SQLException;
/**
* Constructs a {@link Modifier} without a cache invalidator.
*/
public Modifier ()
{
this(null);
}
/**
* Constructs a {@link Modifier} with the given cache invalidator.
*/
public Modifier (CacheInvalidator invalidator)
{
_invalidator = invalidator;