From 35f654a8c1f29e37b81122050893aa3107e883b5 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Sat, 6 Sep 2008 01:54:08 +0000 Subject: [PATCH] Added loadAll() which takes a collection of primary keys. Since we've already got the primary keys, this goes right to phase two wherein we check the cache for hits and load the remaining records by primary key (and cache them). --- .../jdbc/depot/DepotRepository.java | 33 ++++- .../samskivert/jdbc/depot/FindAllQuery.java | 134 ++++++++++++------ 2 files changed, 120 insertions(+), 47 deletions(-) diff --git a/src/java/com/samskivert/jdbc/depot/DepotRepository.java b/src/java/com/samskivert/jdbc/depot/DepotRepository.java index fe096fb..3159160 100644 --- a/src/java/com/samskivert/jdbc/depot/DepotRepository.java +++ b/src/java/com/samskivert/jdbc/depot/DepotRepository.java @@ -143,6 +143,35 @@ public abstract class DepotRepository return _ctx.invoke(new FindOneQuery(_ctx, type, clauses)); } + /** + * Converts the supplied set of raw keys into {@link Key} records so they can easily be passed + * to {@link #loadAll}. We'd just accept a collection of raw keys, but that would have the same + * type erasure as the loadAll version that accepts a collection of keys. Alas. + */ + protected List> makeKeys ( + Class type, Collection> primaryKeys) + throws DatabaseException + { + // convert the raw keys into real key records + DepotMarshaller marsh = _ctx.getMarshaller(type); + List> keys = new ArrayList>(); + for (Comparable key : primaryKeys) { + keys.add(marsh.makePrimaryKey(key)); + } + return keys; + } + + /** + * Loads up all persistent records that match the supplied set of primary keys. + */ + protected List loadAll (Class type, Collection> keys) + throws DatabaseException + { + // if we have precisely one query clause and it's a KeySet, then we can skip the key lookup + // phase and go right to phase two of our two phase query process + return _ctx.invoke(new FindAllQuery.WithKeys(_ctx, type, keys)); + } + /** * A varargs version of {@link #findAll(Class,Collection)}. */ @@ -182,8 +211,8 @@ public abstract class DepotRepository throws DatabaseException { DepotMarshaller marsh = _ctx.getMarshaller(type); - boolean useExplicit = !skipCache || - (marsh.getTableName() == null) || !marsh.hasPrimaryKey() || !_ctx.isUsingCache(); + boolean useExplicit = !skipCache || (marsh.getTableName() == null) || + !marsh.hasPrimaryKey() || !_ctx.isUsingCache(); // queries on @Computed records or the presence of FieldOverrides use the simple algorithm for (QueryClause clause : clauses) { diff --git a/src/java/com/samskivert/jdbc/depot/FindAllQuery.java b/src/java/com/samskivert/jdbc/depot/FindAllQuery.java index 653e98e..7d2cf9d 100644 --- a/src/java/com/samskivert/jdbc/depot/FindAllQuery.java +++ b/src/java/com/samskivert/jdbc/depot/FindAllQuery.java @@ -111,60 +111,45 @@ public abstract class FindAllQuery JDBCUtil.close(stmt); } - // if we're fetching a huge number of records, we have to do it in multiple queries - if (fetchKeys.size() > In.MAX_KEYS) { - int keyCount = fetchKeys.size(); - do { - Set> keys = new HashSet>(); - Iterator> iter = fetchKeys.iterator(); - for (int ii = 0; ii < Math.min(keyCount, In.MAX_KEYS); ii++) { - keys.add(iter.next()); - iter.remove(); - } - keyCount -= keys.size(); - loadRecords(conn, keys, entities); - } while (keyCount > 0); + return loadAndResolve(conn, allKeys, fetchKeys, entities); + } + } - } else if (fetchKeys.size() > 0) { - loadRecords(conn, fetchKeys, entities); - } - - List result = new ArrayList(); - for (Key key : allKeys) { - T value = entities.get(key); - if (value != null) { - result.add(value); - } - } - return result; + /** + * The two-pass collection query implementation. {@see DepotRepository#findAll} for details. + */ + public static class WithKeys extends FindAllQuery + { + public WithKeys (PersistenceContext ctx, Class type, Collection> keys) + throws DatabaseException + { + super(ctx, type); + _keys = keys; } - protected void loadRecords (Connection conn, Set> keys, Map, T> entities) + public List invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { - _builder.newQuery(new SelectClause(_type, _marsh.getFieldNames(), - new KeySet(_type, keys))); - PreparedStatement stmt = _builder.prepare(conn); - try { - ResultSet rs = stmt.executeQuery(); - int cnt = 0, dups = 0; - while (rs.next()) { - T obj = _marsh.createObject(rs); - if (entities.put(_marsh.getPrimaryKey(obj), obj) != null) { - dups++; + Map, T> entities = new HashMap, T>(); + Set> fetchKeys = new HashSet>(); + for (Key key : _keys) { + // TODO: All this cache fiddling needs to move to PersistenceContext? + CacheAdapter.CachedValue hit = _ctx.cacheLookup(key); + if (hit != null) { + T value = hit.getValue(); + if (value != null) { + @SuppressWarnings("unchecked") T newValue = (T) value.clone(); + entities.put(key, newValue); + continue; } - cnt++; } - if (cnt != keys.size()) { - log.warning("Row count mismatch in second pass [query=" + stmt + - ", wanted=" + keys.size() + ", got=" + cnt + - ", dups=" + dups + "]"); - } - - } finally { - JDBCUtil.close(stmt); + fetchKeys.add(key); } + + return loadAndResolve(conn, _keys, fetchKeys, entities); } + + protected Collection> _keys; } /** @@ -241,6 +226,65 @@ public abstract class FindAllQuery return result; } + protected void loadRecords (Connection conn, Set> keys, Map, T> entities) + throws SQLException + { + _builder.newQuery(new SelectClause(_type, _marsh.getFieldNames(), + new KeySet(_type, keys))); + PreparedStatement stmt = _builder.prepare(conn); + try { + ResultSet rs = stmt.executeQuery(); + int cnt = 0, dups = 0; + while (rs.next()) { + T obj = _marsh.createObject(rs); + if (entities.put(_marsh.getPrimaryKey(obj), obj) != null) { + dups++; + } + cnt++; + } + if (cnt != keys.size()) { + log.warning("Row count mismatch in second pass [query=" + stmt + + ", wanted=" + keys.size() + ", got=" + cnt + + ", dups=" + dups + "]"); + } + + } finally { + JDBCUtil.close(stmt); + } + } + + protected List loadAndResolve (Connection conn, Collection> allKeys, + Set> fetchKeys, Map, T> entities) + throws SQLException + { + // if we're fetching a huge number of records, we have to do it in multiple queries + if (fetchKeys.size() > In.MAX_KEYS) { + int keyCount = fetchKeys.size(); + do { + Set> keys = new HashSet>(); + Iterator> iter = fetchKeys.iterator(); + for (int ii = 0; ii < Math.min(keyCount, In.MAX_KEYS); ii++) { + keys.add(iter.next()); + iter.remove(); + } + keyCount -= keys.size(); + loadRecords(conn, keys, entities); + } while (keyCount > 0); + + } else if (fetchKeys.size() > 0) { + loadRecords(conn, fetchKeys, entities); + } + + List result = new ArrayList(); + for (Key key : allKeys) { + T value = entities.get(key); + if (value != null) { + result.add(value); + } + } + return result; + } + protected PersistenceContext _ctx; protected SQLBuilder _builder; protected DepotMarshaller _marsh;