Return null from a load() with no matches, rather than throwing a NoSuchElement

exception. I'm tempted to reinstate the NoSuchElement exception for a load()
that attempts to project fields from the selected rows, but I think it makes
more sense for a load() which aggregates over zero rows to return null than to
throw a NoSuchElement exception.
This commit is contained in:
Michael Bayne
2010-12-09 22:55:49 +00:00
parent d93db7a0f7
commit 1b2df7b583
2 changed files with 33 additions and 10 deletions
@@ -364,48 +364,53 @@ public class QueryBuilder<T extends PersistentRecord>
}
/**
* Returns the value from the first row that matches the configured query clauses.
* Returns the value from the first row that matches the configured query clauses. This value
* may be null if the query clauses match no rows.
*/
public <V> V load (SQLExpression<V> selexp)
{
return select(selexp).get(0); // TODO: revamp FindOneQuery and use that
return getLoaded(select(selexp)); // TODO: revamp FindOneQuery and use that
}
/**
* Returns the value from the first row that matches the configured query clauses.
* Returns the value from the first row that matches the configured query clauses. This value
* may be null if the query clauses match no rows.
*/
public <V1, V2> Tuple2<V1, V2> load (SQLExpression<V1> exp1, SQLExpression<V2> exp2)
{
return select(exp1, exp2).get(0); // TODO: revamp FindOneQuery and use that
return getLoaded(select(exp1, exp2)); // TODO: revamp FindOneQuery and use that
}
/**
* Returns the value from the first row that matches the configured query clauses.
* Returns the value from the first row that matches the configured query clauses. This value
* may be null if the query clauses match no rows.
*/
public <V1, V2, V3> Tuple3<V1, V2, V3> load (
SQLExpression<V1> exp1, SQLExpression<V2> exp2, SQLExpression<V3> exp3)
{
return select(exp1, exp2, exp3).get(0); // TODO: revamp FindOneQuery and use that
return getLoaded(select(exp1, exp2, exp3)); // TODO: revamp FindOneQuery and use that
}
/**
* Returns the value from the first row that matches the configured query clauses.
* Returns the value from the first row that matches the configured query clauses. This value
* may be null if the query clauses match no rows.
*/
public <V1, V2, V3, V4> Tuple4<V1, V2, V3, V4> load (
SQLExpression<V1> exp1, SQLExpression<V2> exp2,
SQLExpression<V3> exp3, SQLExpression<V4> exp4)
{
return select(exp1, exp2, exp3, exp4).get(0); // TODO: revamp FindOneQuery and use that
return getLoaded(select(exp1, exp2, exp3, exp4)); // TODO: revamp FindOneQuery and use that
}
/**
* Returns the value from the first row that matches the configured query clauses.
* Returns the value from the first row that matches the configured query clauses. This value
* may be null if the query clauses match no rows.
*/
public <V1, V2, V3, V4, V5> Tuple5<V1, V2, V3, V4, V5> load (
SQLExpression<V1> exp1, SQLExpression<V2> exp2, SQLExpression<V3> exp3,
SQLExpression<V4> exp4, SQLExpression<V5> exp5)
{
return select(exp1, exp2, exp3, exp4, exp5).get(0); // TODO: use revamped FindOneQuery
return getLoaded(select(exp1, exp2, exp3, exp4, exp5)); // TODO: use revamped FindOneQuery
}
/**
@@ -553,6 +558,11 @@ public class QueryBuilder<T extends PersistentRecord>
checkState(_forUpdate == null, "ForUpdate clause not supported by delete.");
}
protected static <T> T getLoaded (List<T> selections)
{
return selections.isEmpty() ? null : selections.get(0);
}
protected final PersistenceContext _ctx;
protected final DepotRepository _repo;
protected final Class<T> _pclass;
@@ -96,6 +96,19 @@ public class ProjectionTest extends TestBase
assertEquals(20, jdata.size());
}
@Test public void testNoMatches ()
{
QueryBuilder<TestRecord> empty =
_repo.from(TestRecord.class).where(TestRecord.AGE.greaterThan(100));
// test a projection from a query that returns no matches
assertNull(empty.load(TestRecord.RECORD_ID));
assertEquals(0, empty.select().size());
// test an aggregate on a query that matches no rows
assertNull(empty.load(Funcs.max(TestRecord.RECORD_ID)));
}
@Test public void testAggregates ()
{
// test computed expressions on the RHS (the casts are just to cope with JUnit's overloads)