From d74c272e730921bc1f3bd11eeaff5588efe97786 Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Thu, 8 Jun 2017 23:02:12 -0400 Subject: [PATCH] Add ResultSetIterable support to querymapper --- .../jdbc/util/ResultSetIterable.java | 153 ++++++++++++++++++ .../jdbc/util/ResultSetToObject.java | 12 ++ querymapper/genQueryMapper.sh | 10 +- .../jdbc/AbstractRowMapper.java | 14 +- .../jdbc/CachingQueryMapper.java | 12 ++ .../jdbc/CleaningRowToObjectMapper.java | 17 +- .../jdbc/CompilingRowToObjectMapper.java | 13 +- .../moparisthebest/jdbc/ListQueryMapper.java | 22 +++ .../moparisthebest/jdbc/NullQueryMapper.java | 42 +++++ .../com/moparisthebest/jdbc/QueryMapper.java | 52 ++++++ .../moparisthebest/jdbc/ResultSetMapper.java | 34 +++- .../com/moparisthebest/jdbc/RowMapper.java | 4 + .../moparisthebest/jdbc/QueryMapperTest.java | 47 +++++- 13 files changed, 419 insertions(+), 13 deletions(-) create mode 100644 common/src/main/java/com/moparisthebest/jdbc/util/ResultSetIterable.java create mode 100644 common/src/main/java/com/moparisthebest/jdbc/util/ResultSetToObject.java diff --git a/common/src/main/java/com/moparisthebest/jdbc/util/ResultSetIterable.java b/common/src/main/java/com/moparisthebest/jdbc/util/ResultSetIterable.java new file mode 100644 index 0000000..99d0a4b --- /dev/null +++ b/common/src/main/java/com/moparisthebest/jdbc/util/ResultSetIterable.java @@ -0,0 +1,153 @@ +package com.moparisthebest.jdbc.util; + +import java.io.Closeable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import static com.moparisthebest.jdbc.TryClose.tryClose; + +/** + * Iterable/Iterator interface over a ResultSet + *

+ * This has some special semantics the caller must be aware of though: + *

+ * 1. It has to be closed in a finally just like a ResultSet so it can close it's underlying ResultSet + * 2. Call to .iterator() just returns this, meaning you can only loop over it once, if you need to loop multiple times get a List or something + * 3. This guarantees that the ResultSet/Calendar you send into this class with the ResultSetToObject will be the only + * instances sent into the ResultSetToObject.toObject() method, so if you do fancy initialization of some sort you can + * do it in the constructor based on those instances, or once on the first call to .toObject() and cache it forever. + * Or it can be entirely stateless and re-used across multiple ResultSets, your choice. + * 4. If you set a PreparedStatement to close, you can only set it once, and it will close it when you call .close() on this + */ +public class ResultSetIterable implements Iterable, Iterator, Closeable { + + public static final ResultSetIterable EMPTY_RESULT_SET_ITERABLE = new EmptyResultSetIterable(); + + @SuppressWarnings("unchecked") + public static ResultSetIterable emptyResultSetIterable() { + return (ResultSetIterable) EMPTY_RESULT_SET_ITERABLE; + } + + public static ResultSetIterable getResultSetIterable(final ResultSet rs, final ResultSetToObject rsto) { + return getResultSetIterable(rs, rsto, null); + } + + /** + * This is a convenience method meant to be called like this, where rs is a ResultSet + *

+ * ResultSetIterable rsi = ResultSetIterable.getResultSetIterable(rs, rs.next() ? complicatedBuildResultSetToObject(rs) : null, cal); + *

+ * This way you can avoid building or sending in a ResultSetToObject all together if there are no rows, therefore if rsto + * sent into this is null, it returns an EMPTY_RESULT_SET_ITERABLE + *

+ * This assumes rs.next() was called once before sent into this function + */ + @SuppressWarnings("unchecked") + public static ResultSetIterable getResultSetIterable(final ResultSet rs, final ResultSetToObject rsto, final Calendar cal) { + if (rsto == null) + return (ResultSetIterable) EMPTY_RESULT_SET_ITERABLE; + final ResultSetIterable ret = new ResultSetIterable(rs, rsto, cal); + ret.calledNext = true; + return ret; + } + + private final ResultSet rs; + private final Calendar cal; + private final ResultSetToObject rsto; + + private boolean calledNext = false; + private PreparedStatement ps = null; + + protected ResultSetIterable() { + this.rs = null; + this.cal = null; + this.rsto = null; + } + + public ResultSetIterable(final ResultSet rs, final ResultSetToObject rsto) { + this(rs, rsto, null); + } + + public ResultSetIterable(final ResultSet rs, final ResultSetToObject rsto, final Calendar cal) { + if (rs == null || rsto == null) + throw new NullPointerException("rs and rsto must be non-null"); + this.rs = rs; + this.cal = cal; + this.rsto = rsto; + } + + public ResultSetIterable setPreparedStatementToClose(final PreparedStatement ps) { + if (this.ps != null) + throw new IllegalStateException("can only set PreparedStatement to close once"); + this.ps = ps; + return this; + } + + @Override + public Iterator iterator() { + return this; + } + + @Override + public boolean hasNext() { + if (calledNext) + return true; + try { + calledNext = true; + return rs.next(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public T next() { + try { + if (!calledNext) + rs.next(); + else + calledNext = false; + return rsto.toObject(rs, cal); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove unsupported"); + } + + @Override + public void close() { + tryClose(rs); + tryClose(ps); + } + + private static class EmptyResultSetIterable extends ResultSetIterable { + @Override + public ResultSetIterable setPreparedStatementToClose(final PreparedStatement ps) { + tryClose(ps); + return this; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Object next() { + throw new NoSuchElementException("empty ResultSet"); + } + + @Override + public void close() { + // do nothing + } + } +} diff --git a/common/src/main/java/com/moparisthebest/jdbc/util/ResultSetToObject.java b/common/src/main/java/com/moparisthebest/jdbc/util/ResultSetToObject.java new file mode 100644 index 0000000..faa645b --- /dev/null +++ b/common/src/main/java/com/moparisthebest/jdbc/util/ResultSetToObject.java @@ -0,0 +1,12 @@ +package com.moparisthebest.jdbc.util; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Calendar; + +/** + * Created by mopar on 6/9/17. + */ +public interface ResultSetToObject { + T toObject(final ResultSet rs, final Calendar cal) throws SQLException; +} diff --git a/querymapper/genQueryMapper.sh b/querymapper/genQueryMapper.sh index 58d5800..8058312 100755 --- a/querymapper/genQueryMapper.sh +++ b/querymapper/genQueryMapper.sh @@ -22,7 +22,7 @@ function finishFile(){ result="$(prepareFile "src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java")" # single object types -cat src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java | grep public | egrep '(toObject|toSingleMap)\(' | grep ', Calendar cal)' | while read method +cat src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java | grep public | egrep '(toObject|toSingleMap|toResultSetIterable|toResultSetIterableMap)\(' | grep ', Calendar cal)' | while read method do #echo "method: $method" method_name=$(echo $method | egrep -o '[^ ]+\(') @@ -73,6 +73,12 @@ do return cm.$method_name$(echo $method | sed -e 's/^.*(//' -e 's/final //g' -e 's/, [^ ]* /, /g' -e 's/ResultSet rs/bindExecute(ps, bindObjects)/' -e 's/) {/);/') } +EOF + + # handle this specially in QueryMapper because we need it to hold open PreparedStatement until the ResultSetIterable is closed + if [ "$method_name" != 'toResultSetIterable(' -a "$method_name" != 'toResultSetIterableMap(' ]; then + + cat >> "$query" <> "$caching_query" < implements RowMapper { +public abstract class AbstractRowMapper implements RowMapper, ResultSetToObject { /** ResultSet to map. */ protected final ResultSet _resultSet; @@ -110,4 +112,14 @@ public abstract class AbstractRowMapper implements RowMapper { } return this.keys = keys; } + + @Override + public T toObject(final ResultSet rs, final Calendar cal) throws SQLException { + return this.mapRowToReturnType(); + } + + @Override + public ResultSetToObject getResultSetToObject() { + return this; + } } diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java index ffb5c52..8c71e56 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java @@ -1,5 +1,7 @@ package com.moparisthebest.jdbc; +import com.moparisthebest.jdbc.util.ResultSetIterable; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -137,6 +139,16 @@ public class CachingQueryMapper extends QueryMapper { return super.toObject(getPreparedStatement(sql), componentType, bindObjects); } + @Override + public ResultSetIterable toResultSetIterable(String sql, Class componentType, final Object... bindObjects) throws SQLException { + return super.toResultSetIterable(getPreparedStatement(sql), componentType, bindObjects); + } + + @Override + public , V> ResultSetIterable> toResultSetIterableMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return super.toResultSetIterableMap(getPreparedStatement(sql), componentType, mapValType, bindObjects); + } + @Override public , V> Map toSingleMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return super.toSingleMap(getPreparedStatement(sql), componentType, mapValType, bindObjects); diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java index 3252b37..f40a037 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/CleaningRowToObjectMapper.java @@ -1,13 +1,16 @@ package com.moparisthebest.jdbc; +import com.moparisthebest.jdbc.util.ResultSetToObject; + import java.sql.ResultSet; import java.sql.SQLException; import java.util.Calendar; -public class CleaningRowToObjectMapper implements RowMapper { +public class CleaningRowToObjectMapper implements RowMapper, ResultSetToObject { private final Cleaner cleaner; private final RowMapper delegate; + private ResultSetToObject delegateRsto = null; public CleaningRowToObjectMapper(Cleaner cleaner, ResultSet resultSet, Class returnTypeClass, Calendar cal, Class mapValType, Class mapKeyType) { this(cleaner, new RowToObjectMapper(resultSet, returnTypeClass, cal, mapValType, mapKeyType)); @@ -27,4 +30,16 @@ public class CleaningRowToObjectMapper implements RowMapper { public K getMapKey() throws SQLException { return delegate.getMapKey(); } + + @Override + public T toObject(final ResultSet rs, final Calendar cal) throws SQLException { + return cleaner.clean(this.delegateRsto.toObject(rs, cal)); + } + + @Override + public ResultSetToObject getResultSetToObject() { + if(this.delegateRsto == null) + this.delegateRsto = delegate.getResultSetToObject(); + return this; + } } diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java index 4cc596a..13749b0 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/CompilingRowToObjectMapper.java @@ -2,7 +2,6 @@ package com.moparisthebest.jdbc; import com.moparisthebest.classgen.Compiler; -import javax.lang.model.type.TypeMirror; import java.io.IOException; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; @@ -83,6 +82,16 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { return resultSetToObject.getFirstColumn(_resultSet, _cal); } + @Override + public T toObject(final ResultSet rs, final Calendar cal) throws SQLException { + return resultSetToObject.toObject(rs, cal); + } + + @Override + public com.moparisthebest.jdbc.util.ResultSetToObject getResultSetToObject() { + return resultSetToObject; + } + @Override protected String[] getKeysFromResultSet() throws SQLException { if (keys == null) @@ -95,7 +104,7 @@ public class CompilingRowToObjectMapper extends RowToObjectMapper { throw new MapperException("not supported here"); } - public interface ResultSetToObject { + public interface ResultSetToObject extends com.moparisthebest.jdbc.util.ResultSetToObject { K getFirstColumn(final ResultSet rs, final Calendar cal) throws SQLException; T toObject(final ResultSet rs, final Calendar cal) throws SQLException; } diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java index af1ca34..1447a76 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java @@ -1,5 +1,7 @@ package com.moparisthebest.jdbc; +import com.moparisthebest.jdbc.util.ResultSetIterable; + import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; @@ -240,6 +242,26 @@ public class ListQueryMapper extends QueryMapper { return delegate.toObject(prepareSql(sql, bindObjects), componentType, bindObjects); } + @Override + public ResultSetIterable toResultSetIterable(PreparedStatement ps, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toResultSetIterable(ps, componentType, bindObjects); + } + + @Override + public ResultSetIterable toResultSetIterable(String sql, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toResultSetIterable(prepareSql(sql, bindObjects), componentType, bindObjects); + } + + @Override + public , V> ResultSetIterable> toResultSetIterableMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toResultSetIterableMap(ps, componentType, mapValType, bindObjects); + } + + @Override + public , V> ResultSetIterable> toResultSetIterableMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toResultSetIterableMap(prepareSql(sql, bindObjects), componentType, mapValType, bindObjects); + } + @Override public , V> Map toSingleMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return delegate.toSingleMap(ps, componentType, mapValType, bindObjects); diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java index a910fb0..17b157a 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java @@ -1,5 +1,7 @@ package com.moparisthebest.jdbc; +import com.moparisthebest.jdbc.util.ResultSetIterable; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -257,6 +259,46 @@ public class NullQueryMapper extends QueryMapper { return null; } + @Override + public ResultSetIterable toResultSetIterable(PreparedStatement query, Class componentType, final Object... bindObjects) { + try { + return delegate.toResultSetIterable(query, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public ResultSetIterable toResultSetIterable(String query, Class componentType, final Object... bindObjects) { + try { + return delegate.toResultSetIterable(query, componentType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> ResultSetIterable> toResultSetIterableMap(PreparedStatement query, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toResultSetIterableMap(query, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + + @Override + public , V> ResultSetIterable> toResultSetIterableMap(String query, Class componentType, Class mapValType, final Object... bindObjects) { + try { + return delegate.toResultSetIterableMap(query, componentType, mapValType, bindObjects); + } catch (Throwable e) { + if (verbose) e.printStackTrace(); + } + return null; + } + @Override public , V> Map toSingleMap(PreparedStatement query, Class componentType, Class mapValType, final Object... bindObjects) { try { diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java index 7b6240e..94f6b5e 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java @@ -1,5 +1,7 @@ package com.moparisthebest.jdbc; +import com.moparisthebest.jdbc.util.ResultSetIterable; + import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; @@ -338,6 +340,48 @@ public class QueryMapper implements Closeable { } } + // these are handled specially and not generated because we need it to hold open PreparedStatement until the ResultSetIterable is closed + + public ResultSetIterable toResultSetIterable(String sql, Class componentType, final Object... bindObjects) throws SQLException { + boolean error = true; + PreparedStatement ps = null; + ResultSet rs = null; + ResultSetIterable ret = null; + try { + ps = conn.prepareStatement(sql); + rs = this.toResultSet(ps, bindObjects); + ret = cm.toResultSetIterable(rs, componentType).setPreparedStatementToClose(ps); + error = false; + return ret; + } finally { + if (error) { + tryClose(ret); + tryClose(rs); + tryClose(ps); + } + } + } + + public , V> ResultSetIterable> toResultSetIterableMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + boolean error = true; + PreparedStatement ps = null; + ResultSet rs = null; + ResultSetIterable> ret = null; + try { + ps = conn.prepareStatement(sql); + rs = this.toResultSet(ps, bindObjects); + ret = cm.toResultSetIterableMap(rs, componentType, mapValType).setPreparedStatementToClose(ps); + error = false; + return ret; + } finally { + if (error) { + tryClose(ret); + tryClose(rs); + tryClose(ps); + } + } + } + // these are standard getters public ResultSetMapper getCustomResultSetMapper() { @@ -364,6 +408,14 @@ public class QueryMapper implements Closeable { } } + public ResultSetIterable toResultSetIterable(PreparedStatement ps, Class componentType, final Object... bindObjects) throws SQLException { + return cm.toResultSetIterable(bindExecute(ps, bindObjects), componentType); + } + + public , V> ResultSetIterable> toResultSetIterableMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return cm.toResultSetIterableMap(bindExecute(ps, bindObjects), componentType, mapValType); + } + public , V> Map toSingleMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { return cm.toSingleMap(bindExecute(ps, bindObjects), componentType, mapValType); } diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java index e0ce3f0..d1a1224 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java @@ -19,8 +19,11 @@ package com.moparisthebest.jdbc; * $Header:$ */ +import com.moparisthebest.jdbc.util.ResultSetIterable; + import java.lang.ref.SoftReference; -import java.lang.reflect.*; +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -122,12 +125,22 @@ public class ResultSetMapper implements RowMapperProvider { if (rs.next()) ret = getRowMapper(rs, componentType, cal, mapValType, null).mapRowToReturnType(); } catch (SQLException e) { - // ignore + // ignore // todo: this looks crazy dangerous look into it... } tryClose(rs); return ret == null ? RowToObjectMapper.fixNull(componentType) : ret; } + protected ResultSetIterable privToResultSetIterable(ResultSet rs, Class componentType, Calendar cal, Class mapValType) { + try { + return ResultSetIterable.getResultSetIterable(rs, + rs.next() ? getRowMapper(rs, componentType, cal, mapValType, null).getResultSetToObject() : null, + cal); + } catch (SQLException e) { + throw new MapperException("cannot create ResultSetIterable", e); + } + } + protected , E> T privToCollection(ResultSet rs, final Class collectionType, Class componentType, int arrayMaxLength, Calendar cal, Class mapValType) { return privToCollection(rs, instantiateClass(collectionType, ArrayList.class), componentType, arrayMaxLength, cal, mapValType); } @@ -437,6 +450,15 @@ public class ResultSetMapper implements RowMapperProvider { return privToObject(rs, componentType, cal, null); } + public ResultSetIterable toResultSetIterable(ResultSet rs, Class componentType, Calendar cal) { + return privToResultSetIterable(rs, componentType, cal, null); + } + + @SuppressWarnings("unchecked") + public , V> ResultSetIterable> toResultSetIterableMap(ResultSet rs, Class componentType, Class mapValType, Calendar cal) { + return (ResultSetIterable>)privToResultSetIterable(rs, componentType, cal, mapValType); + } + public , E> T toCollection(ResultSet rs, final Class collectionType, Class componentType, int arrayMaxLength, Calendar cal) { return privToCollection(rs, collectionType, componentType, arrayMaxLength, cal, null); } @@ -607,6 +629,14 @@ public class ResultSetMapper implements RowMapperProvider { return this.toObject(rs, componentType, cal); } + public ResultSetIterable toResultSetIterable(ResultSet rs, Class componentType) { + return this.toResultSetIterable(rs, componentType, cal); + } + + public , V> ResultSetIterable> toResultSetIterableMap(ResultSet rs, Class componentType, Class mapValType) { + return this.toResultSetIterableMap(rs, componentType, mapValType, cal); + } + public , V> Map toSingleMap(ResultSet rs, Class componentType, Class mapValType) { return this.toSingleMap(rs, componentType, mapValType, cal); } diff --git a/querymapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java b/querymapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java index beac8c1..c84a062 100644 --- a/querymapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java +++ b/querymapper/src/main/java/com/moparisthebest/jdbc/RowMapper.java @@ -1,5 +1,7 @@ package com.moparisthebest.jdbc; +import com.moparisthebest.jdbc.util.ResultSetToObject; + import java.sql.SQLException; /** @@ -18,4 +20,6 @@ public interface RowMapper { * @throws MapperException if _mapKeyType is null */ K getMapKey() throws SQLException; + + ResultSetToObject getResultSetToObject(); } diff --git a/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java b/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java index cc962e5..f5f52bc 100644 --- a/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java +++ b/querymapper/src/test/java/com/moparisthebest/jdbc/QueryMapperTest.java @@ -1,15 +1,18 @@ package com.moparisthebest.jdbc; import com.moparisthebest.jdbc.dto.*; +import com.moparisthebest.jdbc.util.ResultSetIterable; import org.junit.*; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.SQLException; import java.util.*; import static com.moparisthebest.jdbc.TryClose.tryClose; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -25,6 +28,11 @@ public class QueryMapperTest { public static final Boss fieldBoss1 = new FieldBoss(2, new Date(0), "Second", "Person", "Finance", "Second"); public static final Boss fieldBoss2 = new FieldBoss(3, new Date(0), "Third", "Person", "Finance", null); public static final Boss fieldBoss3 = new FieldBoss(4, new Date(0), null, "Person", "Finance", "Fourth"); + public static final Person fieldPerson2 = new FieldPerson(5, new Date(0), "Second", "Person"); + public static final Person fieldPerson3 = new FieldPerson(6, new Date(0), "Third", "Person"); + + public static final Person[] people = new Person[]{fieldPerson1, fieldPerson2, fieldPerson3}; + public static final Boss[] bosses =new Boss[]{fieldBoss1, fieldBoss2, fieldBoss3}; public static final Person setPerson1 = new SetPerson(fieldPerson1); public static final Boss setBoss1 = new SetBoss(fieldBoss1); @@ -70,9 +78,9 @@ public class QueryMapperTest { qm = new QueryMapper(conn); qm.executeUpdate("CREATE TABLE person (person_no NUMERIC, first_name VARCHAR(40), last_name VARCHAR(40), birth_date TIMESTAMP)"); qm.executeUpdate("CREATE TABLE boss (person_no NUMERIC, department VARCHAR(40))"); - for (final Person person : new Person[]{fieldPerson1}) + for (final Person person : people) qm.executeUpdate("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES (?, ?, ?, ?)", person.getPersonNo(), person.getBirthDate(), person.getLastName(), person.getFirstName()); - for (final Boss boss : new Boss[]{fieldBoss1, fieldBoss2, fieldBoss3}) { + for (final Boss boss : bosses) { qm.executeUpdate("INSERT INTO person (person_no, birth_date, last_name, first_name) VALUES (?, ?, ?, ?)", boss.getPersonNo(), boss.getBirthDate(), boss.getLastName(), boss.getFirstName() == null ? boss.getFirst_name() : boss.getFirstName()); qm.executeUpdate("INSERT INTO boss (person_no, department) VALUES (?, ?)", boss.getPersonNo(), boss.getDepartment()); } @@ -249,7 +257,7 @@ public class QueryMapperTest { @Test public void testSelectArrayMap() throws Throwable { final List> arrayMap = getListMap(); - Assert.assertArrayEquals(arrayMap.toArray(new Map[arrayMap.size()]), qm.toArrayMap("SELECT first_name, last_name FROM person WHERE person_no < 4", arrayMap.get(0).getClass(), String.class)); + assertArrayEquals(arrayMap.toArray(new Map[arrayMap.size()]), qm.toArrayMap("SELECT first_name, last_name FROM person WHERE person_no < 4", arrayMap.get(0).getClass(), String.class)); } @Test @@ -304,19 +312,19 @@ public class QueryMapperTest { @Test public void testSelectLongObjectArray() throws Throwable { final Long[] expected = {fieldPerson1.getPersonNo()}; - Assert.assertArrayEquals(expected, qm.toArray("SELECT person_no FROM person WHERE person_no = ?", Long.class, expected[0])); + assertArrayEquals(expected, qm.toArray("SELECT person_no FROM person WHERE person_no = ?", Long.class, expected[0])); } @Test public void testSelectObjectArray() throws Throwable { final Long[] arr = {1L, 2L, 3L}; - Assert.assertArrayEquals(arr, qm.toObject("SELECT 1, 2, 3 FROM person WHERE person_no = ?", Long[].class, fieldPerson1.getPersonNo())); + assertArrayEquals(arr, qm.toObject("SELECT 1, 2, 3 FROM person WHERE person_no = ?", Long[].class, fieldPerson1.getPersonNo())); } @Test public void testSelectPrimitiveArray() throws Throwable { final long[] arr = {1L, 2L, 3L}; - Assert.assertArrayEquals(arr, qm.toObject("SELECT 1, 2, 3 FROM person WHERE person_no = ?", long[].class, fieldPerson1.getPersonNo())); + assertArrayEquals(arr, qm.toObject("SELECT 1, 2, 3 FROM person WHERE person_no = ?", long[].class, fieldPerson1.getPersonNo())); } @Test(expected = com.moparisthebest.jdbc.MapperException.class) @@ -371,4 +379,31 @@ public class QueryMapperTest { assertNull(map.get("TOM")); } } + + @Test + public void testList() throws SQLException { + final List fromDb = qm.toList("SELECT * from person WHERE person_no IN (?,?,?) ORDER BY person_no", + FieldPerson.class, people[0].getPersonNo(), people[1].getPersonNo(), people[2].getPersonNo()); + assertArrayEquals(people, fromDb.toArray()); + } + + @Test + public void testListQueryMapperList() throws SQLException { + final ListQueryMapper lqm = new ListQueryMapper(qm); + final List fromDb = lqm.toList("SELECT * from person WHERE " + ListQueryMapper.inListReplace + " ORDER BY person_no", + FieldPerson.class, lqm.inList("person_no", Arrays.asList(people[0].getPersonNo(), people[1].getPersonNo(), people[2].getPersonNo()))); + assertArrayEquals(people, fromDb.toArray()); + lqm.close(); + } + + @Test + public void testResultSetIterable() throws SQLException { + final ResultSetIterable rsi = qm.toResultSetIterable("SELECT * from person WHERE person_no IN (?,?,?) ORDER BY person_no", + FieldPerson.class, people[0].getPersonNo(), people[1].getPersonNo(), people[2].getPersonNo()); + final List fromDb = new ArrayList(); + for(final FieldPerson fieldPerson : rsi) + fromDb.add(fieldPerson); + rsi.close(); + assertArrayEquals(people, fromDb.toArray()); + } }