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). git-svn-id: https://samskivert.googlecode.com/svn/trunk@2400 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -143,6 +143,35 @@ public abstract class DepotRepository
|
|||||||
return _ctx.invoke(new FindOneQuery<T>(_ctx, type, clauses));
|
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>)}.
|
* A varargs version of {@link #findAll(Class<T>,Collection<QueryClause>)}.
|
||||||
*/
|
*/
|
||||||
@@ -182,8 +211,8 @@ public abstract class DepotRepository
|
|||||||
throws DatabaseException
|
throws DatabaseException
|
||||||
{
|
{
|
||||||
DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
|
DepotMarshaller<T> marsh = _ctx.getMarshaller(type);
|
||||||
boolean useExplicit = !skipCache ||
|
boolean useExplicit = !skipCache || (marsh.getTableName() == null) ||
|
||||||
(marsh.getTableName() == null) || !marsh.hasPrimaryKey() || !_ctx.isUsingCache();
|
!marsh.hasPrimaryKey() || !_ctx.isUsingCache();
|
||||||
|
|
||||||
// queries on @Computed records or the presence of FieldOverrides use the simple algorithm
|
// queries on @Computed records or the presence of FieldOverrides use the simple algorithm
|
||||||
for (QueryClause clause : clauses) {
|
for (QueryClause clause : clauses) {
|
||||||
|
|||||||
@@ -111,60 +111,45 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
|||||||
JDBCUtil.close(stmt);
|
JDBCUtil.close(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're fetching a huge number of records, we have to do it in multiple queries
|
return loadAndResolve(conn, allKeys, fetchKeys, entities);
|
||||||
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);
|
* The two-pass collection query implementation. {@see DepotRepository#findAll} for details.
|
||||||
}
|
*/
|
||||||
|
public static class WithKeys<T extends PersistentRecord> extends FindAllQuery<T>
|
||||||
List<T> result = new ArrayList<T>();
|
{
|
||||||
for (Key<T> key : allKeys) {
|
public WithKeys (PersistenceContext ctx, Class<T> type, Collection<Key<T>> keys)
|
||||||
T value = entities.get(key);
|
throws DatabaseException
|
||||||
if (value != null) {
|
{
|
||||||
result.add(value);
|
super(ctx, type);
|
||||||
}
|
_keys = keys;
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loadRecords (Connection conn, Set<Key<T>> keys, Map<Key<T>, T> entities)
|
public List<T> invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
_builder.newQuery(new SelectClause<T>(_type, _marsh.getFieldNames(),
|
Map<Key<T>, T> entities = new HashMap<Key<T>, T>();
|
||||||
new KeySet<T>(_type, keys)));
|
Set<Key<T>> fetchKeys = new HashSet<Key<T>>();
|
||||||
PreparedStatement stmt = _builder.prepare(conn);
|
for (Key<T> key : _keys) {
|
||||||
try {
|
// TODO: All this cache fiddling needs to move to PersistenceContext?
|
||||||
ResultSet rs = stmt.executeQuery();
|
CacheAdapter.CachedValue<T> hit = _ctx.cacheLookup(key);
|
||||||
int cnt = 0, dups = 0;
|
if (hit != null) {
|
||||||
while (rs.next()) {
|
T value = hit.getValue();
|
||||||
T obj = _marsh.createObject(rs);
|
if (value != null) {
|
||||||
if (entities.put(_marsh.getPrimaryKey(obj), obj) != null) {
|
@SuppressWarnings("unchecked") T newValue = (T) value.clone();
|
||||||
dups++;
|
entities.put(key, newValue);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
cnt++;
|
|
||||||
}
|
}
|
||||||
if (cnt != keys.size()) {
|
fetchKeys.add(key);
|
||||||
log.warning("Row count mismatch in second pass [query=" + stmt +
|
|
||||||
", wanted=" + keys.size() + ", got=" + cnt +
|
|
||||||
", dups=" + dups + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
JDBCUtil.close(stmt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return loadAndResolve(conn, _keys, fetchKeys, entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Collection<Key<T>> _keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,6 +226,65 @@ public abstract class FindAllQuery<T extends PersistentRecord>
|
|||||||
return result;
|
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 PersistenceContext _ctx;
|
||||||
protected SQLBuilder _builder;
|
protected SQLBuilder _builder;
|
||||||
protected DepotMarshaller<T> _marsh;
|
protected DepotMarshaller<T> _marsh;
|
||||||
|
|||||||
Reference in New Issue
Block a user