Modifications to clone() on insertion and extraction from cache, from Zell. A

whole bunch of type safety jockeying and concretification of QueryClause by
yours truly.
This commit is contained in:
Michael Bayne
2007-01-16 05:30:02 +00:00
parent 4f6a334018
commit 180e4626c1
21 changed files with 230 additions and 196 deletions
@@ -65,7 +65,7 @@ public abstract class ConstructedQuery<T>
* *
* This method is called by individual clauses. * This method is called by individual clauses.
*/ */
public String getTableAbbreviation (Class cl) public String getTableAbbreviation (Class<? extends PersistentRecord> cl)
{ {
int ix = _classList.indexOf(cl); int ix = _classList.indexOf(cl);
if (ix < 0) { if (ix < 0) {
@@ -197,11 +197,13 @@ public abstract class ConstructedQuery<T>
* class, as dictated by the key and query clauses. A persistence context is supplied for * class, as dictated by the key and query clauses. A persistence context is supplied for
* instantiation of marshallers, which may trigger table creations and schema migrations. * instantiation of marshallers, which may trigger table creations and schema migrations.
*/ */
protected ConstructedQuery (PersistenceContext ctx, Class type, QueryClause... clauses) protected ConstructedQuery (PersistenceContext ctx, Class<? extends PersistentRecord> type,
QueryClause... clauses)
throws PersistenceException throws PersistenceException
{ {
_mainType = type; _mainType = type;
Set<Class> classSet = new HashSet<Class>(); Set<Class<? extends PersistentRecord>> classSet =
new HashSet<Class<? extends PersistentRecord>>();
if (type != null) { if (type != null) {
classSet.add(type); classSet.add(type);
} }
@@ -261,17 +263,17 @@ public abstract class ConstructedQuery<T>
} }
_classMap = new HashMap<Class, DepotMarshaller>(); _classMap = new HashMap<Class, DepotMarshaller>();
for (Class<?> c : classSet) { for (Class<? extends PersistentRecord> c : classSet) {
_classMap.put(c, ctx.getMarshaller(c)); _classMap.put(c, ctx.getMarshaller(c));
} }
_classList = new ArrayList<Class>(classSet); _classList = new ArrayList<Class<? extends PersistentRecord>>(classSet);
} }
/** The persistent class to instantiate for the results. */ /** The persistent class to instantiate for the results. */
protected Class _mainType; protected Class<? extends PersistentRecord> _mainType;
/** A list of referenced classes, used to generate table abbreviations. */ /** A list of referenced classes, used to generate table abbreviations. */
protected List<Class> _classList; protected List<Class<? extends PersistentRecord>> _classList;
/** Classes mapped to marshallers, used for table names and field lists. */ /** Classes mapped to marshallers, used for table names and field lists. */
protected Map<Class, DepotMarshaller> _classMap; protected Map<Class, DepotMarshaller> _classMap;
@@ -63,7 +63,7 @@ import static com.samskivert.jdbc.depot.Log.log;
* Handles the marshalling and unmarshalling of persistent instances to JDBC primitives ({@link * Handles the marshalling and unmarshalling of persistent instances to JDBC primitives ({@link
* PreparedStatement} and {@link ResultSet}). * PreparedStatement} and {@link ResultSet}).
*/ */
public class DepotMarshaller<T> public class DepotMarshaller<T extends PersistentRecord>
{ {
/** The name of a private static field that must be defined for all persistent object classes. /** The name of a private static field that must be defined for all persistent object classes.
* It is used to handle schema migration. If automatic schema migration is not desired, define * It is used to handle schema migration. If automatic schema migration is not desired, define
@@ -29,9 +29,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import com.samskivert.io.PersistenceException; import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider; import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.JDBCUtil; import com.samskivert.jdbc.JDBCUtil;
@@ -66,7 +63,8 @@ public class DepotRepository
/** /**
* Loads the persistent object that matches the specified primary key. * Loads the persistent object that matches the specified primary key.
*/ */
protected <T> T load (Class<T> type, Comparable primaryKey, QueryClause... clauses) protected <T extends PersistentRecord> T load (Class<T> type, Comparable primaryKey,
QueryClause... clauses)
throws PersistenceException throws PersistenceException
{ {
clauses = ArrayUtil.append(clauses, _ctx.getMarshaller(type).makePrimaryKey(primaryKey)); clauses = ArrayUtil.append(clauses, _ctx.getMarshaller(type).makePrimaryKey(primaryKey));
@@ -76,7 +74,8 @@ public class DepotRepository
/** /**
* Loads the persistent object that matches the specified primary key. * Loads the persistent object that matches the specified primary key.
*/ */
protected <T> T load (Class<T> type, String ix, Comparable val, QueryClause... clauses) protected <T extends PersistentRecord> T load (Class<T> type, String ix, Comparable val,
QueryClause... clauses)
throws PersistenceException throws PersistenceException
{ {
clauses = ArrayUtil.append(clauses, new Key<T>(type, ix, val)); clauses = ArrayUtil.append(clauses, new Key<T>(type, ix, val));
@@ -86,8 +85,9 @@ public class DepotRepository
/** /**
* Loads the persistent object that matches the specified two-column primary key. * Loads the persistent object that matches the specified two-column primary key.
*/ */
protected <T> T load (Class<T> type, String ix1, Comparable val1, String ix2, protected <T extends PersistentRecord> T load (Class<T> type, String ix1, Comparable val1,
Comparable val2, QueryClause... clauses) String ix2, Comparable val2,
QueryClause... clauses)
throws PersistenceException throws PersistenceException
{ {
clauses = ArrayUtil.append(clauses, new Key<T>(type, ix1, val1, ix2, val2)); clauses = ArrayUtil.append(clauses, new Key<T>(type, ix1, val1, ix2, val2));
@@ -97,8 +97,9 @@ public class DepotRepository
/** /**
* Loads the persistent object that matches the specified three-column primary key. * Loads the persistent object that matches the specified three-column primary key.
*/ */
protected <T> T load (Class<T> type, String ix1, Comparable val1, String ix2, protected <T extends PersistentRecord> T load (Class<T> type, String ix1, Comparable val1,
Comparable val2, String ix3, Comparable val3, QueryClause... clauses) String ix2, Comparable val2, String ix3,
Comparable val3, QueryClause... clauses)
throws PersistenceException throws PersistenceException
{ {
clauses = ArrayUtil.append(clauses, new Key<T>(type, ix1, val1, ix2, val2, ix3, val3)); clauses = ArrayUtil.append(clauses, new Key<T>(type, ix1, val1, ix2, val2, ix3, val3));
@@ -108,7 +109,7 @@ public class DepotRepository
/** /**
* Loads the first persistent object that matches the supplied key. * Loads the first persistent object that matches the supplied key.
*/ */
protected <T> T load (Class<T> type, QueryClause... clauses) protected <T extends PersistentRecord> T load (Class<T> type, QueryClause... clauses)
throws PersistenceException throws PersistenceException
{ {
final DepotMarshaller<T> marsh = _ctx.getMarshaller(type); final DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
@@ -130,6 +131,7 @@ public class DepotRepository
} }
} }
// from Query
public void updateCache (PersistenceContext ctx, T result) { public void updateCache (PersistenceContext ctx, T result) {
CacheKey key = getCacheKey(); CacheKey key = getCacheKey();
if (key == null) { if (key == null) {
@@ -140,7 +142,18 @@ public class DepotRepository
// if we can, create a key from what was actually returned // if we can, create a key from what was actually returned
key = marsh.getPrimaryKey(result); key = marsh.getPrimaryKey(result);
} }
ctx.cacheStore(key, result); ctx.cacheStore(key, result != null ? result.clone() : null);
}
// from Query
public T transformCacheHit (CacheKey key, T value)
{
// we do not want to return a reference to the actual cached entity
if (value == null) {
return null;
}
@SuppressWarnings("unchecked") T cvalue = (T) value.clone();
return cvalue;
} }
}); });
} }
@@ -148,7 +161,7 @@ public class DepotRepository
/** /**
* Loads all persistent objects that match the specified key. * Loads all persistent objects that match the specified key.
*/ */
protected <T,C extends Collection<T>> Collection<T> findAll ( protected <T extends PersistentRecord, C extends Collection<T>> Collection<T> findAll (
Class<T> type, QueryClause... clauses) Class<T> type, QueryClause... clauses)
throws PersistenceException throws PersistenceException
{ {
@@ -169,13 +182,33 @@ public class DepotRepository
} }
} }
// from Query
public void updateCache (PersistenceContext ctx, ArrayList<T> result) { public void updateCache (PersistenceContext ctx, ArrayList<T> result) {
if (marsh.hasPrimaryKey()) { if (marsh.hasPrimaryKey()) {
for (T bit : result) { for (T bit : result) {
ctx.cacheStore(marsh.getPrimaryKey(bit), bit); ctx.cacheStore(marsh.getPrimaryKey(bit), bit.clone());
} }
} }
} }
// from Query
public ArrayList<T> transformCacheHit (CacheKey key, ArrayList<T> bits)
{
if (bits == null) {
return bits;
}
ArrayList<T> result = new ArrayList<T>();
for (T bit : bits) {
if (bit != null) {
@SuppressWarnings("unchecked") T cbit = (T) bit.clone();
result.add(cbit);
} else {
result.add(null);
}
}
return result;
}
}); });
} }
@@ -185,7 +218,7 @@ public class DepotRepository
* *
* @return the number of rows modified by this action, this should always be one. * @return the number of rows modified by this action, this should always be one.
*/ */
protected <T> int insert (final T record) protected <T extends PersistentRecord> int insert (T record)
throws PersistenceException throws PersistenceException
{ {
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
@@ -194,12 +227,12 @@ public class DepotRepository
return _ctx.invoke(new CachingModifier<T>(record, key, key) { return _ctx.invoke(new CachingModifier<T>(record, key, key) {
public int invoke (Connection conn) throws SQLException { public int invoke (Connection conn) throws SQLException {
// update our modifier's key so that it can cache our results // update our modifier's key so that it can cache our results
updateKey(marsh.assignPrimaryKey(conn, record, false)); updateKey(marsh.assignPrimaryKey(conn, _result, false));
PreparedStatement stmt = marsh.createInsert(conn, record); PreparedStatement stmt = marsh.createInsert(conn, _result);
try { try {
int mods = stmt.executeUpdate(); int mods = stmt.executeUpdate();
// check again in case we have a post-factum key generator // check again in case we have a post-factum key generator
updateKey(marsh.assignPrimaryKey(conn, record, true)); updateKey(marsh.assignPrimaryKey(conn, _result, true));
return mods; return mods;
} finally { } finally {
JDBCUtil.close(stmt); JDBCUtil.close(stmt);
@@ -214,7 +247,7 @@ public class DepotRepository
* *
* @return the number of rows modified by this action. * @return the number of rows modified by this action.
*/ */
protected <T> int update (final T record) protected <T extends PersistentRecord> int update (T record)
throws PersistenceException throws PersistenceException
{ {
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
@@ -224,7 +257,7 @@ public class DepotRepository
} }
return _ctx.invoke(new CachingModifier<T>(record, key, key) { return _ctx.invoke(new CachingModifier<T>(record, key, key) {
public int invoke (Connection conn) throws SQLException { public int invoke (Connection conn) throws SQLException {
PreparedStatement stmt = marsh.createUpdate(conn, record, key); PreparedStatement stmt = marsh.createUpdate(conn, _result, key);
try { try {
return stmt.executeUpdate(); return stmt.executeUpdate();
} finally { } finally {
@@ -240,7 +273,7 @@ public class DepotRepository
* *
* @return the number of rows modified by this action. * @return the number of rows modified by this action.
*/ */
protected <T> int update (final T record, final String... modifiedFields) protected <T extends PersistentRecord> int update (T record, final String... modifiedFields)
throws PersistenceException throws PersistenceException
{ {
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
@@ -250,7 +283,7 @@ public class DepotRepository
} }
return _ctx.invoke(new CachingModifier<T>(record, key, key) { return _ctx.invoke(new CachingModifier<T>(record, key, key) {
public int invoke (Connection conn) throws SQLException { public int invoke (Connection conn) throws SQLException {
PreparedStatement stmt = marsh.createUpdate(conn, record, key, modifiedFields); PreparedStatement stmt = marsh.createUpdate(conn, _result, key, modifiedFields);
try { try {
return stmt.executeUpdate(); return stmt.executeUpdate();
} finally { } finally {
@@ -270,8 +303,8 @@ public class DepotRepository
* *
* @return the number of rows modified by this action. * @return the number of rows modified by this action.
*/ */
protected <T> int updatePartial (Class<T> type, Comparable primaryKey, protected <T extends PersistentRecord> int updatePartial (
Map<String,Object> updates) Class<T> type, Comparable primaryKey, Map<String,Object> updates)
throws PersistenceException throws PersistenceException
{ {
Object[] fieldsValues = new Object[updates.size()*2]; Object[] fieldsValues = new Object[updates.size()*2];
@@ -293,7 +326,8 @@ public class DepotRepository
* *
* @return the number of rows modified by this action. * @return the number of rows modified by this action.
*/ */
protected <T> int updatePartial (Class<T> type, Comparable primaryKey, Object... fieldsValues) protected <T extends PersistentRecord> int updatePartial (
Class<T> type, Comparable primaryKey, Object... fieldsValues)
throws PersistenceException throws PersistenceException
{ {
Key<T> key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey); Key<T> key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey);
@@ -312,8 +346,8 @@ public class DepotRepository
* *
* @return the number of rows modified by this action. * @return the number of rows modified by this action.
*/ */
protected <T> int updatePartial (Class<T> type, final Where key, CacheInvalidator invalidator, protected <T extends PersistentRecord> int updatePartial (
Object... fieldsValues) Class<T> type, final Where key, CacheInvalidator invalidator, Object... fieldsValues)
throws PersistenceException throws PersistenceException
{ {
// separate the arguments into keys and values // separate the arguments into keys and values
@@ -354,7 +388,8 @@ public class DepotRepository
* *
* @return the number of rows modified by this action. * @return the number of rows modified by this action.
*/ */
protected <T> int updateLiteral (Class<T> type, Comparable primaryKey, String... fieldsValues) protected <T extends PersistentRecord> int updateLiteral (
Class<T> type, Comparable primaryKey, String... fieldsValues)
throws PersistenceException throws PersistenceException
{ {
Key<T> key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey); Key<T> key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey);
@@ -378,8 +413,8 @@ public class DepotRepository
* *
* @return the number of rows modified by this action. * @return the number of rows modified by this action.
*/ */
protected <T> int updateLiteral (Class<T> type, final Where key, CacheInvalidator invalidator, protected <T extends PersistentRecord> int updateLiteral (
String... fieldsValues) Class<T> type, final Where key, CacheInvalidator invalidator, String... fieldsValues)
throws PersistenceException throws PersistenceException
{ {
// separate the arguments into keys and values // separate the arguments into keys and values
@@ -410,7 +445,7 @@ public class DepotRepository
* *
* @return the number of rows modified by this action, this should always be one. * @return the number of rows modified by this action, this should always be one.
*/ */
protected <T> int store (final T record) protected <T extends PersistentRecord> int store (T record)
throws PersistenceException throws PersistenceException
{ {
final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass()); final DepotMarshaller marsh = _ctx.getMarshaller(record.getClass());
@@ -422,7 +457,7 @@ public class DepotRepository
// if our primary key isn't null, update rather than insert the record // if our primary key isn't null, update rather than insert the record
// before been persisted and insert // before been persisted and insert
if (key != null) { if (key != null) {
stmt = marsh.createUpdate(conn, record, key); stmt = marsh.createUpdate(conn, _result, key);
int mods = stmt.executeUpdate(); int mods = stmt.executeUpdate();
if (mods > 0) { if (mods > 0) {
return mods; return mods;
@@ -432,10 +467,10 @@ public class DepotRepository
// if the update modified zero rows or the primary key was obviously unset, do // if the update modified zero rows or the primary key was obviously unset, do
// an insertion // an insertion
updateKey(marsh.assignPrimaryKey(conn, record, false)); updateKey(marsh.assignPrimaryKey(conn, _result, false));
stmt = marsh.createInsert(conn, record); stmt = marsh.createInsert(conn, _result);
int mods = stmt.executeUpdate(); int mods = stmt.executeUpdate();
updateKey(marsh.assignPrimaryKey(conn, record, true)); updateKey(marsh.assignPrimaryKey(conn, _result, true));
return mods; return mods;
} finally { } finally {
@@ -451,7 +486,7 @@ public class DepotRepository
* *
* @return the number of rows deleted by this action. * @return the number of rows deleted by this action.
*/ */
protected <T> int delete (T record) protected <T extends PersistentRecord> int delete (T record)
throws PersistenceException throws PersistenceException
{ {
@SuppressWarnings("unchecked") Class<T> type = (Class<T>)record.getClass(); @SuppressWarnings("unchecked") Class<T> type = (Class<T>)record.getClass();
@@ -468,7 +503,7 @@ public class DepotRepository
* *
* @return the number of rows deleted by this action. * @return the number of rows deleted by this action.
*/ */
protected <T> int delete (Class<T> type, Comparable primaryKeyValue) protected <T extends PersistentRecord> int delete (Class<T> type, Comparable primaryKeyValue)
throws PersistenceException throws PersistenceException
{ {
Key<T> primaryKey = _ctx.getMarshaller(type).makePrimaryKey(primaryKeyValue); Key<T> primaryKey = _ctx.getMarshaller(type).makePrimaryKey(primaryKeyValue);
@@ -480,7 +515,8 @@ public class DepotRepository
* *
* @return the number of rows deleted by this action. * @return the number of rows deleted by this action.
*/ */
protected <T> int deleteAll (Class<T> type, final Where key, CacheInvalidator invalidator) protected <T extends PersistentRecord> int deleteAll (
Class<T> type, final Where key, CacheInvalidator invalidator)
throws PersistenceException throws PersistenceException
{ {
final DepotMarshaller marsh = _ctx.getMarshaller(type); final DepotMarshaller marsh = _ctx.getMarshaller(type);
+9 -6
View File
@@ -23,9 +23,9 @@ package com.samskivert.jdbc.depot;
import java.io.Serializable; import java.io.Serializable;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import com.samskivert.jdbc.depot.clause.Where; import com.samskivert.jdbc.depot.clause.Where;
@@ -37,7 +37,7 @@ import com.samskivert.jdbc.depot.clause.Where;
* itself upon modification. This class is created by many {@link DepotMarshaller} methods as * itself upon modification. This class is created by many {@link DepotMarshaller} methods as
* a convenience, and may also be instantiated explicitly. * a convenience, and may also be instantiated explicitly.
*/ */
public class Key<T> extends Where public class Key<T extends PersistentRecord> extends Where
implements CacheKey, CacheInvalidator implements CacheKey, CacheInvalidator
{ {
/** /**
@@ -94,13 +94,16 @@ public class Key<T> extends Where
} }
// from QueryClause // from QueryClause
public List<Class> getClassSet() public Collection<Class<? extends PersistentRecord>> getClassSet ()
{ {
return Arrays.asList(new Class[] { _pClass }); ArrayList<Class<? extends PersistentRecord>> set =
new ArrayList<Class<? extends PersistentRecord>>();
set.add(_pClass);
return set;
} }
// from QueryClause // from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder) public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{ {
builder.append(" where "); builder.append(" where ");
boolean first = true; boolean first = true;
@@ -59,7 +59,7 @@ public abstract class Modifier
* presuming both _key and _result are non-null. These variables may be set or modified * 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. * during execution in addition to being supplied to the constructor.
*/ */
public static abstract class CachingModifier<T> extends Modifier public static abstract class CachingModifier<T extends PersistentRecord> extends Modifier
{ {
/** /**
* Construct a new CachingModifier with the given result, cache key, and invalidator, * Construct a new CachingModifier with the given result, cache key, and invalidator,
@@ -89,7 +89,7 @@ public abstract class Modifier
super.cacheUpdate(ctx); super.cacheUpdate(ctx);
// if we have both a key and a record, cache // if we have both a key and a record, cache
if (_key != null && _result != null) { if (_key != null && _result != null) {
ctx.cacheStore(_key, _result); ctx.cacheStore(_key, _result.clone());
} }
} }
@@ -22,9 +22,9 @@ package com.samskivert.jdbc.depot;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import com.samskivert.jdbc.depot.clause.Where; import com.samskivert.jdbc.depot.clause.Where;
@@ -34,7 +34,7 @@ import com.samskivert.jdbc.depot.clause.Where;
* does not implement {@link CacheKey} but it does implement {@link CacheInvalidator} which means * does not implement {@link CacheKey} but it does implement {@link CacheInvalidator} which means
* it can be sent into e.g. {@link DepotRepository#deleteAll) and have it clean up after itself. * it can be sent into e.g. {@link DepotRepository#deleteAll) and have it clean up after itself.
*/ */
public class MultiKey<T> extends Where public class MultiKey<T extends PersistentRecord> extends Where
implements CacheInvalidator implements CacheInvalidator
{ {
/** /**
@@ -85,13 +85,16 @@ public class MultiKey<T> extends Where
} }
// from QueryClause // from QueryClause
public List<Class> getClassSet() public Collection<Class<? extends PersistentRecord>> getClassSet ()
{ {
return Arrays.asList(new Class[] { _pClass }); ArrayList<Class<? extends PersistentRecord>> set =
new ArrayList<Class<? extends PersistentRecord>>();
set.add(_pClass);
return set;
} }
// from QueryClause // from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder) public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{ {
builder.append(" where "); builder.append(" where ");
boolean first = true; boolean first = true;
@@ -140,7 +140,7 @@ public class PersistenceContext
* and register a pre-migration for every single schema migration and they will then be * and register a pre-migration for every single schema migration and they will then be
* guaranteed to be run in registration order and with predictable pre- and post-conditions. * guaranteed to be run in registration order and with predictable pre- and post-conditions.
*/ */
public <T> void registerMigration (Class<T> type, EntityMigration migration) public <T extends PersistentRecord> void registerMigration (Class<T> type, EntityMigration migration)
{ {
getRawMarshaller(type).registerMigration(migration); getRawMarshaller(type).registerMigration(migration);
} }
@@ -149,7 +149,7 @@ public class PersistenceContext
* Returns the marshaller for the specified persistent object class, creating and initializing * Returns the marshaller for the specified persistent object class, creating and initializing
* it if necessary. * it if necessary.
*/ */
public <T> DepotMarshaller<T> getMarshaller (Class<T> type) public <T extends PersistentRecord> DepotMarshaller<T> getMarshaller (Class<T> type)
throws PersistenceException throws PersistenceException
{ {
DepotMarshaller<T> marshaller = getRawMarshaller(type); DepotMarshaller<T> marshaller = getRawMarshaller(type);
@@ -176,7 +176,11 @@ public class PersistenceContext
if (cacheHit != null) { if (cacheHit != null) {
Log.debug("invoke: cache hit [hit=" + cacheHit + "]"); Log.debug("invoke: cache hit [hit=" + cacheHit + "]");
@SuppressWarnings("unchecked") T value = (T) cacheHit.getValue(); @SuppressWarnings("unchecked") T value = (T) cacheHit.getValue();
return value; value = query.transformCacheHit(key, value);
if (value != null) {
return value;
}
Log.debug("invoke: transformCacheHit returned null; rejecting cached value.");
} }
} }
Log.debug("invoke: cache miss [key=" + key + "]"); Log.debug("invoke: cache miss [key=" + key + "]");
@@ -337,7 +341,7 @@ public class PersistenceContext
* Looks up and creates, but does not initialize, the marshaller for the specified Entity * Looks up and creates, but does not initialize, the marshaller for the specified Entity
* type. * type.
*/ */
protected <T> DepotMarshaller<T> getRawMarshaller (Class<T> type) protected <T extends PersistentRecord> DepotMarshaller<T> getRawMarshaller (Class<T> type)
{ {
@SuppressWarnings("unchecked") DepotMarshaller<T> marshaller = @SuppressWarnings("unchecked") DepotMarshaller<T> marshaller =
(DepotMarshaller<T>)_marshallers.get(type); (DepotMarshaller<T>)_marshallers.get(type);
@@ -0,0 +1,41 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc.depot;
import java.io.Serializable;
/**
* The base class for all persistent records used in Depot. Persistent records must be cloneable
* and serializable; this class is used to enforce those requirements.
*/
public class PersistentRecord
implements Cloneable, Serializable
{
@Override // from Object
public PersistentRecord clone ()
{
try {
return (PersistentRecord) super.clone();
} catch (CloneNotSupportedException cnse) {
throw new RuntimeException(cnse); // this should never happen
}
}
}
@@ -38,9 +38,15 @@ public interface Query<T>
*/ */
public T invoke (Connection conn) public T invoke (Connection conn)
throws SQLException; throws SQLException;
/**
* Overriden by subclasses to perform special operations when the query would return a cache
* hit. The value may be mutated, modified, or null may be return to force a database hit.
*/
public T transformCacheHit (CacheKey key, T value);
/** /**
* Overriden by subclasses to perform case-by-case cache updates. * Overriden by subclasses to perform case-by-case cache updates.
*/ */
void updateCache (PersistenceContext ctx, T result); public void updateCache (PersistenceContext ctx, T result);
} }
@@ -20,12 +20,9 @@
package com.samskivert.jdbc.depot.clause; package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.io.PersistenceException; import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.ColumnExp; import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.LiteralExp; import com.samskivert.jdbc.depot.expression.LiteralExp;
import com.samskivert.jdbc.depot.expression.SQLExpression; import com.samskivert.jdbc.depot.expression.SQLExpression;
@@ -38,8 +35,7 @@ import com.samskivert.jdbc.depot.expression.SQLExpression;
* table through a {@link ColumnExp}, or a literal expression such as COUNT(*) through a * table through a {@link ColumnExp}, or a literal expression such as COUNT(*) through a
* {@link LiteralExp}. * {@link LiteralExp}.
*/ */
public class FieldOverride public class FieldOverride extends QueryClause
implements QueryClause
{ {
public FieldOverride (String field, String str) public FieldOverride (String field, String str)
throws PersistenceException throws PersistenceException
@@ -47,7 +43,7 @@ public class FieldOverride
this(field, new LiteralExp(str)); this(field, new LiteralExp(str));
} }
public FieldOverride (String field, Class pClass, String pCol) public FieldOverride (String field, Class<? extends PersistentRecord> pClass, String pCol)
throws PersistenceException throws PersistenceException
{ {
this(field, new ColumnExp(pClass, pCol)); this(field, new ColumnExp(pClass, pCol));
@@ -69,25 +65,12 @@ public class FieldOverride
} }
// from QueryClause // from QueryClause
public Collection<Class> getClassSet () public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{
return null;
}
// from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder)
{ {
_override.appendExpression(query, builder); _override.appendExpression(query, builder);
builder.append(" as ").append(_field); builder.append(" as ").append(_field);
} }
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return argIdx;
}
/** The name of the field on the persistent object to override. */ /** The name of the field on the persistent object to override. */
protected String _field; protected String _field;
@@ -20,34 +20,16 @@
package com.samskivert.jdbc.depot.clause; package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
/** /**
* Represents a FOR UPDATE clause. * Represents a FOR UPDATE clause.
*/ */
public class ForUpdate public class ForUpdate extends QueryClause
implements QueryClause
{ {
// from QueryClause // from QueryClause
public Collection<Class> getClassSet () public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{
return null;
}
// from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder)
{ {
builder.append(" for update "); builder.append(" for update ");
} }
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return argIdx;
}
} }
@@ -20,53 +20,45 @@
package com.samskivert.jdbc.depot.clause; package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement; import java.util.ArrayList;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import com.samskivert.io.PersistenceException; import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.util.CollectionUtil;
/** /**
* Completely overrides the FROM clause, if it exists. * Completely overrides the FROM clause, if it exists.
*/ */
public class FromOverride public class FromOverride extends QueryClause
implements QueryClause
{ {
public FromOverride (Class... fromClasses) public FromOverride (Class<? extends PersistentRecord>... fromClasses)
throws PersistenceException throws PersistenceException
{ {
_fromClasses = fromClasses; CollectionUtil.addAll(_fromClasses, fromClasses);
} }
// from QueryClause // from QueryClause
public Collection<Class> getClassSet () public Collection<Class<? extends PersistentRecord>> getClassSet ()
{ {
return Arrays.asList(_fromClasses); return _fromClasses;
} }
// from QueryClause // from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder) public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{ {
builder.append(" from " ); builder.append(" from " );
for (int ii = 0; ii < _fromClasses.length; ii++) { for (int ii = 0; ii < _fromClasses.size(); ii++) {
if (ii > 0) { if (ii > 0) {
builder.append(", "); builder.append(", ");
} }
builder.append(query.getTableName(_fromClasses[ii])). builder.append(query.getTableName(_fromClasses.get(ii))).
append(" as "). append(" as ").append(query.getTableAbbreviation(_fromClasses.get(ii)));
append(query.getTableAbbreviation(_fromClasses[ii]));
} }
} }
// from QueryClause
public int bindArguments (PreparedStatement pstmt, int argIdx)
throws SQLException
{
return argIdx;
}
/** The classes of the tables we're selecting from. */ /** The classes of the tables we're selecting from. */
protected Class[] _fromClasses; protected ArrayList<Class<? extends PersistentRecord>> _fromClasses =
new ArrayList<Class<? extends PersistentRecord>>();
} }
@@ -22,7 +22,6 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.expression.SQLExpression; import com.samskivert.jdbc.depot.expression.SQLExpression;
@@ -30,8 +29,7 @@ import com.samskivert.jdbc.depot.expression.SQLExpression;
/** /**
* Represents a GROUP BY clause. * Represents a GROUP BY clause.
*/ */
public class GroupBy public class GroupBy extends QueryClause
implements QueryClause
{ {
public GroupBy (SQLExpression... values) public GroupBy (SQLExpression... values)
{ {
@@ -39,13 +37,7 @@ public class GroupBy
} }
// from QueryClause // from QueryClause
public Collection<Class> getClassSet () public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{
return null;
}
// from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder)
{ {
builder.append(" group by "); builder.append(" group by ");
for (int ii = 0; ii < _values.length; ii++) { for (int ii = 0; ii < _values.length; ii++) {
@@ -22,22 +22,23 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import com.samskivert.io.PersistenceException; import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.ColumnExp; import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.operator.SQLOperator;
import com.samskivert.jdbc.depot.operator.Conditionals.*; import com.samskivert.jdbc.depot.operator.Conditionals.*;
import com.samskivert.jdbc.depot.operator.SQLOperator;
/** /**
* Represents a JOIN -- currently just an INNER one. * Represents a JOIN -- currently just an INNER one.
*/ */
public class Join public class Join extends QueryClause
implements QueryClause
{ {
public Join (Class pClass, String pCol, Class joinClass, String jCol) public Join (Class<? extends PersistentRecord> pClass, String pCol,
Class<? extends PersistentRecord> joinClass, String jCol)
throws PersistenceException throws PersistenceException
{ {
_joinClass = joinClass; _joinClass = joinClass;
@@ -51,20 +52,23 @@ public class Join
_joinCondition = new Equals(primary, join); _joinCondition = new Equals(primary, join);
} }
public Join (Class joinClass, SQLOperator joinCondition) public Join (Class<? extends PersistentRecord> joinClass, SQLOperator joinCondition)
{ {
_joinClass = joinClass; _joinClass = joinClass;
_joinCondition = joinCondition; _joinCondition = joinCondition;
} }
// from QueryClause // from QueryClause
public Collection<Class> getClassSet () public Collection<Class<? extends PersistentRecord>> getClassSet ()
{ {
return Arrays.asList(new Class[] { _joinClass }); ArrayList<Class<? extends PersistentRecord>> set =
new ArrayList<Class<? extends PersistentRecord>>();
set.add(_joinClass);
return set;
} }
// from QueryClause // from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder) public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{ {
builder.append(" inner join " ); builder.append(" inner join " );
builder.append(query.getTableName(_joinClass)).append(" as "); builder.append(query.getTableName(_joinClass)).append(" as ");
@@ -80,7 +84,7 @@ public class Join
} }
/** The class of the table we're to join against. */ /** The class of the table we're to join against. */
protected Class _joinClass; protected Class<? extends PersistentRecord> _joinClass;
/** The condition used to join in the new table. */ /** The condition used to join in the new table. */
protected SQLOperator _joinCondition; protected SQLOperator _joinCondition;
@@ -22,15 +22,13 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
/** /**
* Represents a LIMIT/OFFSET clause, for pagination. * Represents a LIMIT/OFFSET clause, for pagination.
*/ */
public class Limit public class Limit extends QueryClause
implements QueryClause
{ {
public Limit (int offset, int count) public Limit (int offset, int count)
{ {
@@ -39,13 +37,7 @@ public class Limit
} }
// from QueryClause // from QueryClause
public Collection<Class> getClassSet () public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{
return null;
}
// from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder)
{ {
builder.append(" limit ? offset ? "); builder.append(" limit ? offset ? ");
} }
@@ -22,7 +22,6 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.expression.ColumnExp; import com.samskivert.jdbc.depot.expression.ColumnExp;
@@ -31,8 +30,7 @@ import com.samskivert.jdbc.depot.expression.SQLExpression;
/** /**
* Represents an ORDER BY clause. * Represents an ORDER BY clause.
*/ */
public class OrderBy public class OrderBy extends QueryClause
implements QueryClause
{ {
/** Indicates the order of the clause. */ /** Indicates the order of the clause. */
public enum Order { ASC, DESC }; public enum Order { ASC, DESC };
@@ -76,13 +74,7 @@ public class OrderBy
} }
// from QueryClause // from QueryClause
public Collection<Class> getClassSet () public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{
return null;
}
// from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder)
{ {
builder.append(" order by "); builder.append(" order by ");
for (int ii = 0; ii < _values.length; ii++) { for (int ii = 0; ii < _values.length; ii++) {
@@ -25,29 +25,36 @@ import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.PersistentRecord;
/** /**
* Represents a piece or modifier of an SQL query. * Represents a piece or modifier of an SQL query.
*/ */
public interface QueryClause public abstract class QueryClause
{ {
/** /**
* Return a set of all persistent classes referenced by this clause. Null may be returned if * Return a set of all persistent classes referenced by this clause. The default implementation
* no classes are rererenced. * returns null to indicate that no classes are rererenced.
*/ */
public Collection<Class> getClassSet (); public Collection<Class<? extends PersistentRecord>> getClassSet ()
{
return null;
}
/** /**
* Construct the SQL form of this query clause. The implementor is expected to call methods * Construct the SQL form of this query clause. The implementor is expected to call methods on
* on the Query object to e.g. resolve current table abbreviations associated with classes. * the Query object to e.g. resolve current table abbreviations associated with classes.
*/ */
public void appendClause (ConstructedQuery query, StringBuilder builder); public abstract void appendClause (ConstructedQuery<?> query, StringBuilder builder);
/** /**
* Bind any objects that were referenced in the generated SQL. For each ? that appears in the * Bind any objects that were referenced in the generated SQL. For each ? that appears in the
* SQL, precisely one parameter must be claimed and bound in this method, and argIdx * SQL, precisely one parameter must be claimed and bound in this method, and argIdx
* incremented and returned. * incremented and returned. The default implementation binds nothing.
*/ */
public int bindArguments (PreparedStatement pstmt, int argIdx) public int bindArguments (PreparedStatement pstmt, int argIdx)
throws SQLException; throws SQLException
{
return argIdx;
}
} }
@@ -22,24 +22,22 @@ package com.samskivert.jdbc.depot.clause;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.clause.QueryClause; import com.samskivert.jdbc.depot.clause.QueryClause;
import com.samskivert.jdbc.depot.expression.ColumnExp; import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.ValueExp; import com.samskivert.jdbc.depot.expression.ValueExp;
import com.samskivert.jdbc.depot.operator.SQLOperator;
import com.samskivert.jdbc.depot.operator.Conditionals.Equals; import com.samskivert.jdbc.depot.operator.Conditionals.Equals;
import com.samskivert.jdbc.depot.operator.Conditionals.IsNull; import com.samskivert.jdbc.depot.operator.Conditionals.IsNull;
import com.samskivert.jdbc.depot.operator.Logic.And; import com.samskivert.jdbc.depot.operator.Logic.And;
import com.samskivert.jdbc.depot.operator.SQLOperator;
/** /**
* Represents a where clause: the condition can be any comparison operator or logical combination * Represents a where clause: the condition can be any comparison operator or logical combination
* thereof. * thereof.
*/ */
public class Where public class Where extends QueryClause
implements QueryClause
{ {
public Where (String index, Comparable value) public Where (String index, Comparable value)
{ {
@@ -88,13 +86,7 @@ public class Where
} }
// from QueryClause // from QueryClause
public List<Class> getClassSet() public void appendClause (ConstructedQuery<?> query, StringBuilder builder)
{
return Arrays.asList(new Class[] { });
}
// from QueryClause
public void appendClause (ConstructedQuery query, StringBuilder builder)
{ {
builder.append(" where "); builder.append(" where ");
_condition.appendExpression(query, builder); _condition.appendExpression(query, builder);
@@ -24,6 +24,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.PersistentRecord;
/** /**
* An expression identifying a column of a class, e.g. GameRecord.itemId. If no class is given, * An expression identifying a column of a class, e.g. GameRecord.itemId. If no class is given,
@@ -33,7 +34,7 @@ public class ColumnExp
implements SQLExpression implements SQLExpression
{ {
/** The table that hosts the column we reference, or null. */ /** The table that hosts the column we reference, or null. */
final public Class pClass; final public Class<? extends PersistentRecord> pClass;
/** The name of the column we reference. */ /** The name of the column we reference. */
final public String pColumn; final public String pColumn;
@@ -43,7 +44,7 @@ public class ColumnExp
this(null, column); this(null, column);
} }
public ColumnExp (Class c, String column) public ColumnExp (Class<? extends PersistentRecord> c, String column)
{ {
super(); super();
pClass = c; pClass = c;
@@ -51,7 +52,7 @@ public class ColumnExp
} }
// from SQLExpression // from SQLExpression
public void appendExpression (ConstructedQuery query, StringBuilder builder) public void appendExpression (ConstructedQuery<?> query, StringBuilder builder)
{ {
if (pClass == null || query == null) { if (pClass == null || query == null) {
builder.append(pColumn); builder.append(pColumn);
@@ -34,7 +34,7 @@ public interface SQLExpression
* Construct the SQL form of this expression. The implementor is invited to call methods on the * Construct the SQL form of this expression. The implementor is invited to call methods on the
* Query object to e.g. resolve the current table abbreviations associated with classes. * Query object to e.g. resolve the current table abbreviations associated with classes.
*/ */
public void appendExpression (ConstructedQuery query, StringBuilder builder); public void appendExpression (ConstructedQuery<?> query, StringBuilder builder);
/** /**
* Bind any objects that were referenced in the generated SQL. For each ? that appears in the * Bind any objects that were referenced in the generated SQL. For each ? that appears in the
@@ -25,6 +25,7 @@ import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
import com.samskivert.jdbc.depot.ConstructedQuery; import com.samskivert.jdbc.depot.ConstructedQuery;
import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.ColumnExp; import com.samskivert.jdbc.depot.expression.ColumnExp;
import com.samskivert.jdbc.depot.expression.SQLExpression; import com.samskivert.jdbc.depot.expression.SQLExpression;
import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator; import com.samskivert.jdbc.depot.operator.SQLOperator.BinaryOperator;
@@ -45,7 +46,7 @@ public abstract class Conditionals
this(new ColumnExp(null, pColumn)); this(new ColumnExp(null, pColumn));
} }
public IsNull (Class pClass, String pColumn) public IsNull (Class<? extends PersistentRecord> pClass, String pColumn)
{ {
this(new ColumnExp(pClass, pColumn)); this(new ColumnExp(pClass, pColumn));
} }
@@ -161,12 +162,13 @@ public abstract class Conditionals
this(new ColumnExp(null, pColumn), values.toArray(new Comparable[values.size()])); this(new ColumnExp(null, pColumn), values.toArray(new Comparable[values.size()]));
} }
public In (Class pClass, String pColumn, Comparable... values) public In (Class<? extends PersistentRecord> pClass, String pColumn, Comparable... values)
{ {
this(new ColumnExp(pClass, pColumn), values); this(new ColumnExp(pClass, pColumn), values);
} }
public In (Class pClass, String pColumn, Collection<? extends Comparable> values) public In (Class<? extends PersistentRecord> pClass, String pColumn,
Collection<? extends Comparable> values)
{ {
this(new ColumnExp(pClass, pColumn), values.toArray(new Comparable[values.size()])); this(new ColumnExp(pClass, pColumn), values.toArray(new Comparable[values.size()]));
} }