Added a mechanism for selecting results into a simple result class that relies

only on a matching public constructor. This version is not type-safe, but I'll
also be adding an additional mechanism to do selections like this that uses a
type-safe builder approach, which comes at the cost of a healthy dose of
boilerplate code.
This commit is contained in:
Michael Bayne
2010-12-10 21:12:57 +00:00
parent dd6dec7b9a
commit 4b59373559
3 changed files with 63 additions and 5 deletions
@@ -465,6 +465,15 @@ public class Query<T extends PersistentRecord>
getClauses()));
}
/**
* Returns just the supplied expression from the rows matching the query.
*/
public <V> List<V> selectInto (Class<V> resultClass, SQLExpression<?>... selexps)
{
Projector<T,V> proj = Projector.create(_pclass, resultClass, selexps);
return _ctx.invoke(new FindAllQuery.Projection<T,V>(_ctx, proj, getClauses()));
}
/**
* Deletes the records that match the configured query clauses. Note that only the where
* clauses are used to evaluate a deletion. Attempts to use other clauses will result in
@@ -20,6 +20,10 @@
package com.samskivert.depot.impl;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.expression.SQLExpression;
import com.samskivert.depot.util.*; // TupleN
@@ -33,7 +37,7 @@ public abstract class Projector<T extends PersistentRecord,R>
public static <T extends PersistentRecord, V> Projector<T,V> create (
Class<T> ptype, SQLExpression<V> column)
{
return new Projector<T, V>(ptype, new SQLExpression[] { column }) {
return new Projector<T, V>(ptype, new SQLExpression<?>[] { column }) {
public V createObject (Object[] results) {
@SuppressWarnings("unchecked") V result = (V)results[0];
return result;
@@ -44,7 +48,7 @@ public abstract class Projector<T extends PersistentRecord,R>
public static <T extends PersistentRecord, V1, V2> Projector<T,Tuple2<V1,V2>> create (
Class<T> ptype, SQLExpression<V1> col1, SQLExpression<V2> col2)
{
return new Projector<T, Tuple2<V1,V2>>(ptype, new SQLExpression[] { col1, col2 }) {
return new Projector<T, Tuple2<V1,V2>>(ptype, new SQLExpression<?>[] { col1, col2 }) {
public Tuple2<V1,V2> createObject (Object[] results) {
@SuppressWarnings("unchecked") V1 r1 = (V1)results[0];
@SuppressWarnings("unchecked") V2 r2 = (V2)results[1];
@@ -56,7 +60,8 @@ public abstract class Projector<T extends PersistentRecord,R>
public static <T extends PersistentRecord, V1, V2, V3> Projector<T,Tuple3<V1,V2,V3>> create (
Class<T> ptype, SQLExpression<V1> col1, SQLExpression<V2> col2, SQLExpression<V3> col3)
{
return new Projector<T, Tuple3<V1,V2,V3>>(ptype, new SQLExpression[] { col1, col2, col3 }) {
return new Projector<T, Tuple3<V1,V2,V3>>(
ptype, new SQLExpression<?>[] { col1, col2, col3 }) {
public Tuple3<V1,V2,V3> createObject (Object[] results) {
@SuppressWarnings("unchecked") V1 r1 = (V1)results[0];
@SuppressWarnings("unchecked") V2 r2 = (V2)results[1];
@@ -72,7 +77,7 @@ public abstract class Projector<T extends PersistentRecord,R>
SQLExpression<V4> col4)
{
return new Projector<T, Tuple4<V1,V2,V3,V4>>(
ptype, new SQLExpression[] { col1, col2, col3, col4 }) {
ptype, new SQLExpression<?>[] { col1, col2, col3, col4 }) {
public Tuple4<V1,V2,V3,V4> createObject (Object[] results) {
@SuppressWarnings("unchecked") V1 r1 = (V1)results[0];
@SuppressWarnings("unchecked") V2 r2 = (V2)results[1];
@@ -89,7 +94,7 @@ public abstract class Projector<T extends PersistentRecord,R>
SQLExpression<V4> col4, SQLExpression<V5> col5)
{
return new Projector<T, Tuple5<V1,V2,V3,V4,V5>>(
ptype, new SQLExpression[] { col1, col2, col3, col4, col5 }) {
ptype, new SQLExpression<?>[] { col1, col2, col3, col4, col5 }) {
public Tuple5<V1,V2,V3,V4,V5> createObject (Object[] results) {
@SuppressWarnings("unchecked") V1 r1 = (V1)results[0];
@SuppressWarnings("unchecked") V2 r2 = (V2)results[1];
@@ -101,6 +106,31 @@ public abstract class Projector<T extends PersistentRecord,R>
};
}
public static <T extends PersistentRecord, V> Projector<T,V> create (
Class<T> ptype, final Class<V> resultType, SQLExpression<?>... selexps)
{
return new Projector<T, V>(ptype, selexps) {
public V createObject (Object[] results) {
try {
return _ctor.newInstance(results);
} catch (InstantiationException e) {
throw new DatabaseException("Invalid constructor supplied for projection", e);
} catch (IllegalAccessException e) {
throw new DatabaseException("Invalid constructor supplied for projection", e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new DatabaseException("Error constructing result object", t);
}
}
}
@SuppressWarnings("unchecked")
protected Constructor<V> _ctor = (Constructor<V>)resultType.getConstructors()[0];
};
}
public final Class<T> ptype;
public final SQLExpression<?>[] selexps;
@@ -195,6 +195,25 @@ public class ProjectionTest extends TestBase
// MathFuncs.trunc(TestRecord.AWESOMENESS)).doubleValue(), 0.0001);
}
@Test public void testSelectInfo ()
{
assertEquals("[0 Elvis, 1 Elvis]",
_repo.from(TestRecord.class).where(TestRecord.RECORD_ID.lessThan(2)).
selectInto(IdName.class, TestRecord.RECORD_ID, TestRecord.NAME).toString());
}
protected static class IdName {
public final int id;
public final String name;
public IdName (int id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return id + " " + name;
}
}
// the HSQL in-memory database persists for the lifetime of the VM, which means we have to
// clean up after ourselves in every test; thus we go ahead and share a repository
protected TestRepository _repo = createTestRepository();