diff --git a/src/java/com/samskivert/depot/FindAllQuery.java b/src/java/com/samskivert/depot/FindAllQuery.java index 5e60761..9a152d9 100644 --- a/src/java/com/samskivert/depot/FindAllQuery.java +++ b/src/java/com/samskivert/depot/FindAllQuery.java @@ -75,56 +75,68 @@ public abstract class FindAllQuery extends Query(_type, _marsh.getPrimaryKeyFields(), clauses); - _builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select)); - _builder.newQuery(_select); + _qkey = new SimpleCacheKey(_marsh.getTableName() + "Query", _select.toString()); } @Override // from Query public List getCachedResult (PersistenceContext ctx) { - return null; // TODO + if (_qkey == null) { + return null; + } + _keys = ctx.>cacheLookup(_qkey); + if (_keys == null) { + return null; + } + _cachedQueries++; + _fetchKeys = loadFromCache(ctx, _keys, _entities); + return (_fetchKeys.size() == 0) ? resolve(_keys, _entities) : null; } // from Query public List invoke (PersistenceContext ctx, Connection conn, DatabaseLiaison liaison) throws SQLException { - Map, T> entities = Maps.newHashMap(); - List> allKeys = Lists.newArrayList(); - Set> fetchKeys = Sets.newHashSet(); + // we want this to remain null if our key set came from the cache + String stmtString = null; - // first load up the primary keys on which we're operating - PreparedStatement stmt = _builder.prepare(conn); - String stmtString = stmt.toString(); // for debugging - try { - ResultSet rs = stmt.executeQuery(); - while (rs.next()) { - Key key = _marsh.makePrimaryKey(rs); - allKeys.add(key); + // if we didn't find our key set in the cache, load the keys that match + if (_keys == null) { + List> keys = Lists.newArrayList(); + SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select)); + builder.newQuery(_select); + PreparedStatement stmt = builder.prepare(conn); + stmtString = stmt.toString(); // for debugging + try { + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + keys.add(_marsh.makePrimaryKey(rs)); + } + } finally { + JDBCUtil.close(stmt); } - } finally { - JDBCUtil.close(stmt); - } - _uncachedQueries++; - - // now fetch any records we can from the cache - loadFromCache(ctx, allKeys, entities, fetchKeys); - _cachedRecords = entities.size(); - _uncachedRecords = fetchKeys.size(); - - if (PersistenceContext.CACHE_DEBUG) { - log.info("Loaded " + _marsh.getTableName(), "query", _select, - "keys", keysToString(allKeys)); + _keys = KeySet.newKeySet(_type, keys); + _uncachedQueries++; + if (PersistenceContext.CACHE_DEBUG) { + log.info("Loaded " + _marsh.getTableName() + " keys", "query", _select, + "keys", keysToString(_keys), "cacheable", (_qkey != null)); + } + if (_qkey != null) { + ctx.cacheStore(_qkey, _keys); // cache the resulting key set + } + // and fetch any records we can from the cache + _fetchKeys = loadFromCache(ctx, _keys, _entities); } // finally load the rest from the database - return loadAndResolve(ctx, conn, allKeys, fetchKeys, entities, stmtString); + return loadAndResolve(ctx, conn, _keys, _fetchKeys, _entities, stmtString); } - protected CacheKey getCacheKey () - { - return null; - } + protected SimpleCacheKey _qkey; + protected SelectClause _select; + protected KeySet _keys; + protected Set> _fetchKeys; + protected Map, T> _entities = Maps.newHashMap(); } /** @@ -138,16 +150,13 @@ public abstract class FindAllQuery extends Query getCachedResult (PersistenceContext ctx) { // look up what we can from the cache - loadFromCache(ctx, _keys, _entities, _fetchKeys); - _cachedRecords = _entities.size(); - _uncachedRecords = _fetchKeys.size(); + _fetchKeys = loadFromCache(ctx, _keys, _entities); // if we found everything, we can just return our result straight away, yay! return _fetchKeys.isEmpty() ? resolve(_keys, _entities) : null; @@ -161,8 +170,8 @@ public abstract class FindAllQuery extends Query> _keys; + protected Set> _fetchKeys; protected Map, T> _entities = Maps.newHashMap(); - protected Set> _fetchKeys = Sets.newHashSet(); } /** @@ -177,8 +186,6 @@ public abstract class FindAllQuery extends Query(type, _marsh.getFieldNames(), clauses); - _builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select)); - _builder.newQuery(_select); } @Override // from Query @@ -193,7 +200,9 @@ public abstract class FindAllQuery extends Query result = Lists.newArrayList(); - PreparedStatement stmt = _builder.prepare(conn); + SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select)); + builder.newQuery(_select); + PreparedStatement stmt = builder.prepare(conn); try { ResultSet rs = stmt.executeQuery(); while (rs.next()) { @@ -211,6 +220,8 @@ public abstract class FindAllQuery extends Query _select; } // from Query @@ -226,9 +237,10 @@ public abstract class FindAllQuery extends Query> allKeys, - Map, T> entities, Set> fetchKeys) + protected Set> loadFromCache (PersistenceContext ctx, Iterable> allKeys, + Map, T> entities) { + Set> fetchKeys = Sets.newHashSet(); for (Key key : allKeys) { T value = ctx.cacheLookup(key); if (value != null) { @@ -238,6 +250,12 @@ public abstract class FindAllQuery extends Query resolve (Iterable> allKeys, Map, T> entities) @@ -287,9 +305,11 @@ public abstract class FindAllQuery extends Query(_type, _marsh.getFieldNames(), - KeySet.newKeySet(_type, keys))); - PreparedStatement stmt = _builder.prepare(conn); + SelectClause select = new SelectClause( + _type, _marsh.getFieldNames(), KeySet.newKeySet(_type, keys)); + SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, select)); + builder.newQuery(select); + PreparedStatement stmt = builder.prepare(conn); try { Set> got = Sets.newHashSet(); ResultSet rs = stmt.executeQuery(); @@ -300,10 +320,7 @@ public abstract class FindAllQuery extends Query extends Query keys.size() || (origStmt != null && cnt < keys.size())) { log.warning("Row count mismatch in second pass", "origQuery", origStmt, - "wanted", keys, "got", got, "dups", dups, new Exception()); + // we need toString() here or StringUtil will get smart and dump our + // KeySet using its iterator which results in verbosity + "wanted", KeySet.newKeySet(_type, keys).toString(), + "got", KeySet.newKeySet(_type, got).toString(), + "dups", dups, new Exception()); } if (PersistenceContext.CACHE_DEBUG) { @@ -335,9 +356,7 @@ public abstract class FindAllQuery extends Query _marsh; protected Class _type; - protected SelectClause _select; + protected DepotMarshaller _marsh; protected int _cachedQueries, _uncachedQueries, _cachedRecords, _uncachedRecords; } diff --git a/src/java/com/samskivert/depot/Key.java b/src/java/com/samskivert/depot/Key.java index 10f55ea..d9523e7 100644 --- a/src/java/com/samskivert/depot/Key.java +++ b/src/java/com/samskivert/depot/Key.java @@ -189,7 +189,7 @@ public class Key extends WhereClause // from CacheKey public Serializable getCacheKey () { - return _values; + return this; } // from ValidatingCacheInvalidator diff --git a/src/java/com/samskivert/depot/KeySet.java b/src/java/com/samskivert/depot/KeySet.java index bde2d32..aa2c50c 100644 --- a/src/java/com/samskivert/depot/KeySet.java +++ b/src/java/com/samskivert/depot/KeySet.java @@ -20,6 +20,7 @@ package com.samskivert.depot; +import java.io.Serializable; import java.util.AbstractCollection; import java.util.Arrays; import java.util.Collection; @@ -48,7 +49,7 @@ import static com.samskivert.depot.Log.log; * the cache. */ public abstract class KeySet extends WhereClause - implements SQLExpression, ValidatingCacheInvalidator, Iterable> + implements Serializable, SQLExpression, ValidatingCacheInvalidator, Iterable> { /** * Creates a key set for the supplied persistent record and keys. diff --git a/src/java/com/samskivert/depot/SimpleCacheKey.java b/src/java/com/samskivert/depot/SimpleCacheKey.java index c9b95be..3ac034b 100644 --- a/src/java/com/samskivert/depot/SimpleCacheKey.java +++ b/src/java/com/samskivert/depot/SimpleCacheKey.java @@ -22,6 +22,8 @@ package com.samskivert.depot; import java.io.Serializable; +import com.samskivert.util.ObjectUtil; + /** * Convenience class that implements {@link CacheKey} as simply as possibly. This class is * typically used when the caller wants to cache a non-obvious query such as a collection, @@ -85,21 +87,8 @@ public class SimpleCacheKey return false; } SimpleCacheKey other = (SimpleCacheKey) obj; - if (_cacheId == null) { - if (other._cacheId != null) { - return false; - } - } else if (!_cacheId.equals(other._cacheId)) { - return false; - } - if (_cacheKey == null) { - if (other._cacheKey != null) { - return false; - } - } else if (!_cacheKey.equals(other._cacheKey)) { - return false; - } - return true; + return ObjectUtil.equals(_cacheId, other._cacheId) && + ObjectUtil.equals(_cacheKey, other._cacheKey); } @Override diff --git a/src/java/com/samskivert/depot/tests/TestRepository.java b/src/java/com/samskivert/depot/tests/TestRepository.java index 5e047da..a3f109e 100644 --- a/src/java/com/samskivert/depot/tests/TestRepository.java +++ b/src/java/com/samskivert/depot/tests/TestRepository.java @@ -27,9 +27,10 @@ import java.util.Set; import com.google.common.collect.Sets; +import com.samskivert.jdbc.StaticConnectionProvider; import com.samskivert.util.RandomUtil; -import com.samskivert.jdbc.StaticConnectionProvider; +import com.samskivert.depot.CacheAdapter; import com.samskivert.depot.DepotRepository; import com.samskivert.depot.Key; import com.samskivert.depot.KeySet; @@ -40,6 +41,7 @@ import com.samskivert.depot.annotation.Computed; import com.samskivert.depot.clause.Where; import com.samskivert.depot.expression.LiteralExp; import com.samskivert.depot.operator.Conditionals; +import com.samskivert.depot.util.TestCacheAdapter; /** * A test tool for the Depot repository services. @@ -60,7 +62,8 @@ public class TestRepository extends DepotRepository throws Exception { PersistenceContext perCtx = new PersistenceContext(); - perCtx.init("test", new StaticConnectionProvider("depot.properties"), null); + perCtx.init("test", new StaticConnectionProvider("depot.properties"), + new TestCacheAdapter()); // tests a bogus rename migration // perCtx.registerMigration(TestRecord.class, new SchemaMigration.Rename(1, "foo", "bar")); @@ -71,6 +74,7 @@ public class TestRepository extends DepotRepository TestRepository repo = new TestRepository(perCtx); + System.out.println("Deleting old record."); repo.delete(TestRecord.class, 1); Date now = new Date(System.currentTimeMillis()); @@ -86,7 +90,7 @@ public class TestRepository extends DepotRepository record.numbers = new int[] { 9, 0, 2, 1, 0 }; repo.insert(record); - System.out.println(repo.load(TestRecord.class, record.recordId)); + System.out.println("Record: " + repo.load(TestRecord.class, record.recordId)); // record.age = 25; // record.name = "Bob"; @@ -96,7 +100,7 @@ public class TestRepository extends DepotRepository repo.updatePartial(TestRecord.class, record.recordId, TestRecord.AGE, 25, TestRecord.NAME, "Bob", TestRecord.NUMBERS, new int[] { 1, 2, 3, 4, 5 }); - System.out.println(repo.load(TestRecord.class, record.recordId)); + System.out.println("Updated " + repo.load(TestRecord.class, record.recordId)); for (int ii = 2; ii < CREATE_RECORDS; ii++) { record = new TestRecord(); @@ -116,6 +120,11 @@ public class TestRepository extends DepotRepository System.out.println("Load none " + repo.loadAll(none.toCollection()) + "."); System.out.println("Delete none " + repo.deleteAll(TestRecord.class, none) + "."); + // test collection caching + Where where = new Where(new Conditionals.GreaterThan(TestRecord.RECORD_ID_C, 100)); + System.out.println("100 and up: " + repo.findAll(TestRecord.class, where).size()); + System.out.println("100 and up again: " + repo.findAll(TestRecord.class, where).size()); + // test a partial key set KeySet some = KeySet.newSimpleKeySet( TestRecord.class, Sets.newHashSet(1, 3, 5, 7, 9));