From 4b5937355959f0c3dc83a7617cebd6bb98711e47 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 10 Dec 2010 21:12:57 +0000 Subject: [PATCH] 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. --- src/main/java/com/samskivert/depot/Query.java | 9 +++++ .../com/samskivert/depot/impl/Projector.java | 40 ++++++++++++++++--- .../com/samskivert/depot/ProjectionTest.java | 19 +++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/samskivert/depot/Query.java b/src/main/java/com/samskivert/depot/Query.java index 56f2bc8..63882be 100644 --- a/src/main/java/com/samskivert/depot/Query.java +++ b/src/main/java/com/samskivert/depot/Query.java @@ -465,6 +465,15 @@ public class Query getClauses())); } + /** + * Returns just the supplied expression from the rows matching the query. + */ + public List selectInto (Class resultClass, SQLExpression... selexps) + { + Projector proj = Projector.create(_pclass, resultClass, selexps); + return _ctx.invoke(new FindAllQuery.Projection(_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 diff --git a/src/main/java/com/samskivert/depot/impl/Projector.java b/src/main/java/com/samskivert/depot/impl/Projector.java index dd4acac..10b6ae1 100644 --- a/src/main/java/com/samskivert/depot/impl/Projector.java +++ b/src/main/java/com/samskivert/depot/impl/Projector.java @@ -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 public static Projector create ( Class ptype, SQLExpression column) { - return new Projector(ptype, new SQLExpression[] { column }) { + return new Projector(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 public static Projector> create ( Class ptype, SQLExpression col1, SQLExpression col2) { - return new Projector>(ptype, new SQLExpression[] { col1, col2 }) { + return new Projector>(ptype, new SQLExpression[] { col1, col2 }) { public Tuple2 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 public static Projector> create ( Class ptype, SQLExpression col1, SQLExpression col2, SQLExpression col3) { - return new Projector>(ptype, new SQLExpression[] { col1, col2, col3 }) { + return new Projector>( + ptype, new SQLExpression[] { col1, col2, col3 }) { public Tuple3 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 SQLExpression col4) { return new Projector>( - ptype, new SQLExpression[] { col1, col2, col3, col4 }) { + ptype, new SQLExpression[] { col1, col2, col3, col4 }) { public Tuple4 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 SQLExpression col4, SQLExpression col5) { return new Projector>( - ptype, new SQLExpression[] { col1, col2, col3, col4, col5 }) { + ptype, new SQLExpression[] { col1, col2, col3, col4, col5 }) { public Tuple5 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 }; } + public static Projector create ( + Class ptype, final Class resultType, SQLExpression... selexps) + { + return new Projector(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 _ctor = (Constructor)resultType.getConstructors()[0]; + }; + } + public final Class ptype; public final SQLExpression[] selexps; diff --git a/src/test/java/com/samskivert/depot/ProjectionTest.java b/src/test/java/com/samskivert/depot/ProjectionTest.java index f060d15..6ca9ddd 100644 --- a/src/test/java/com/samskivert/depot/ProjectionTest.java +++ b/src/test/java/com/samskivert/depot/ProjectionTest.java @@ -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();