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).
This commit is contained in:
Michael Bayne
2008-09-06 01:54:08 +00:00
parent 52de64001d
commit 35f654a8c1
2 changed files with 120 additions and 47 deletions
@@ -143,6 +143,35 @@ public abstract class DepotRepository
return _ctx.invoke(new FindOneQuery<T>(_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 <T extends PersistentRecord> List<Key<T>> makeKeys (
Class<T> type, Collection<Comparable<?>> primaryKeys)
throws DatabaseException
{
// convert the raw keys into real key records
DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
List<Key<T>> keys = new ArrayList<Key<T>>();
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 <T extends PersistentRecord> List<T> loadAll (Class<T> type, Collection<Key<T>> 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<T>(_ctx, type, keys));
}
/**
* A varargs version of {@link #findAll(Class<T>,Collection<QueryClause>)}.
*/
@@ -182,8 +211,8 @@ public abstract class DepotRepository
throws DatabaseException
{
DepotMarshaller<T> 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) {
@@ -111,60 +111,45 @@ public abstract class FindAllQuery<T extends PersistentRecord>
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<Key<T>> keys = new HashSet<Key<T>>();
Iterator<Key<T>> 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<T> result = new ArrayList<T>();
for (Key<T> 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<T extends PersistentRecord> extends FindAllQuery<T>
{
public WithKeys (PersistenceContext ctx, Class<T> type, Collection<Key<T>> keys)
throws DatabaseException
{
super(ctx, type);
_keys = keys;
}
protected void loadRecords (Connection conn, Set<Key<T>> keys, Map<Key<T>, T> entities)
public List<T> invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException
{
_builder.newQuery(new SelectClause<T>(_type, _marsh.getFieldNames(),
new KeySet<T>(_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<Key<T>, T> entities = new HashMap<Key<T>, T>();
Set<Key<T>> fetchKeys = new HashSet<Key<T>>();
for (Key<T> key : _keys) {
// TODO: All this cache fiddling needs to move to PersistenceContext?
CacheAdapter.CachedValue<T> 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<Key<T>> _keys;
}
/**
@@ -241,6 +226,65 @@ public abstract class FindAllQuery<T extends PersistentRecord>
return result;
}
protected void loadRecords (Connection conn, Set<Key<T>> keys, Map<Key<T>, T> entities)
throws SQLException
{
_builder.newQuery(new SelectClause<T>(_type, _marsh.getFieldNames(),
new KeySet<T>(_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<T> loadAndResolve (Connection conn, Collection<Key<T>> allKeys,
Set<Key<T>> fetchKeys, Map<Key<T>, 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<Key<T>> keys = new HashSet<Key<T>>();
Iterator<Key<T>> 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<T> result = new ArrayList<T>();
for (Key<T> key : allKeys) {
T value = entities.get(key);
if (value != null) {
result.add(value);
}
}
return result;
}
protected PersistenceContext _ctx;
protected SQLBuilder _builder;
protected DepotMarshaller<T> _marsh;