mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-11-25 02:12:22 -05:00
Add ResultSetIterable support to querymapper
This commit is contained in:
parent
d80a9325d7
commit
d74c272e73
@ -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
|
||||||
|
* <p>
|
||||||
|
* This has some special semantics the caller must be aware of though:
|
||||||
|
* <p>
|
||||||
|
* 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<T> implements Iterable<T>, Iterator<T>, Closeable {
|
||||||
|
|
||||||
|
public static final ResultSetIterable EMPTY_RESULT_SET_ITERABLE = new EmptyResultSetIterable();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> ResultSetIterable<T> emptyResultSetIterable() {
|
||||||
|
return (ResultSetIterable<T>) EMPTY_RESULT_SET_ITERABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ResultSetIterable<T> getResultSetIterable(final ResultSet rs, final ResultSetToObject<T> rsto) {
|
||||||
|
return getResultSetIterable(rs, rsto, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience method meant to be called like this, where rs is a ResultSet
|
||||||
|
* <p>
|
||||||
|
* ResultSetIterable<T> rsi = ResultSetIterable.getResultSetIterable(rs, rs.next() ? complicatedBuildResultSetToObject(rs) : null, cal);
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* This assumes rs.next() was called once before sent into this function
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> ResultSetIterable<T> getResultSetIterable(final ResultSet rs, final ResultSetToObject<T> rsto, final Calendar cal) {
|
||||||
|
if (rsto == null)
|
||||||
|
return (ResultSetIterable<T>) EMPTY_RESULT_SET_ITERABLE;
|
||||||
|
final ResultSetIterable<T> ret = new ResultSetIterable<T>(rs, rsto, cal);
|
||||||
|
ret.calledNext = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ResultSet rs;
|
||||||
|
private final Calendar cal;
|
||||||
|
private final ResultSetToObject<T> 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<T> rsto) {
|
||||||
|
this(rs, rsto, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultSetIterable(final ResultSet rs, final ResultSetToObject<T> 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<T> 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<T> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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> {
|
||||||
|
T toObject(final ResultSet rs, final Calendar cal) throws SQLException;
|
||||||
|
}
|
@ -22,7 +22,7 @@ function finishFile(){
|
|||||||
result="$(prepareFile "src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java")"
|
result="$(prepareFile "src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java")"
|
||||||
|
|
||||||
# single object types
|
# 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
|
do
|
||||||
#echo "method: $method"
|
#echo "method: $method"
|
||||||
method_name=$(echo $method | egrep -o '[^ ]+\(')
|
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/) {/);/')
|
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" <<EOF
|
||||||
$(echo $method | sed -e 's/ResultSet rs/String sql/' -e 's/) {/, final Object... bindObjects) throws SQLException {/')
|
$(echo $method | sed -e 's/ResultSet rs/String sql/' -e 's/) {/, final Object... bindObjects) throws SQLException {/')
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
@ -85,6 +91,8 @@ do
|
|||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
fi # end special case toResultSetIterable
|
||||||
|
|
||||||
# CachingQueryMapper.java
|
# CachingQueryMapper.java
|
||||||
cat >> "$caching_query" <<EOF
|
cat >> "$caching_query" <<EOF
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetToObject;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@ -33,7 +35,7 @@ import java.util.Calendar;
|
|||||||
* RowMapperFactory.
|
* RowMapperFactory.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractRowMapper<K, T> implements RowMapper<K,T> {
|
public abstract class AbstractRowMapper<K, T> implements RowMapper<K,T>, ResultSetToObject<T> {
|
||||||
|
|
||||||
/** ResultSet to map. */
|
/** ResultSet to map. */
|
||||||
protected final ResultSet _resultSet;
|
protected final ResultSet _resultSet;
|
||||||
@ -110,4 +112,14 @@ public abstract class AbstractRowMapper<K, T> implements RowMapper<K,T> {
|
|||||||
}
|
}
|
||||||
return this.keys = keys;
|
return this.keys = keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T toObject(final ResultSet rs, final Calendar cal) throws SQLException {
|
||||||
|
return this.mapRowToReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultSetToObject<T> getResultSetToObject() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
@ -137,6 +139,16 @@ public class CachingQueryMapper extends QueryMapper {
|
|||||||
return super.toObject(getPreparedStatement(sql), componentType, bindObjects);
|
return super.toObject(getPreparedStatement(sql), componentType, bindObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(String sql, Class<T> componentType, final Object... bindObjects) throws SQLException {
|
||||||
|
return super.toResultSetIterable(getPreparedStatement(sql), componentType, bindObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(String sql, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
|
return super.toResultSetIterableMap(getPreparedStatement(sql), componentType, mapValType, bindObjects);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(String sql, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(String sql, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
return super.toSingleMap(getPreparedStatement(sql), componentType, mapValType, bindObjects);
|
return super.toSingleMap(getPreparedStatement(sql), componentType, mapValType, bindObjects);
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetToObject;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
public class CleaningRowToObjectMapper<K, T> implements RowMapper<K, T> {
|
public class CleaningRowToObjectMapper<K, T> implements RowMapper<K, T>, ResultSetToObject<T> {
|
||||||
|
|
||||||
private final Cleaner<T> cleaner;
|
private final Cleaner<T> cleaner;
|
||||||
private final RowMapper<K,T> delegate;
|
private final RowMapper<K,T> delegate;
|
||||||
|
private ResultSetToObject<T> delegateRsto = null;
|
||||||
|
|
||||||
public CleaningRowToObjectMapper(Cleaner<T> cleaner, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
public CleaningRowToObjectMapper(Cleaner<T> cleaner, ResultSet resultSet, Class<T> returnTypeClass, Calendar cal, Class<?> mapValType, Class<K> mapKeyType) {
|
||||||
this(cleaner, new RowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType));
|
this(cleaner, new RowToObjectMapper<K, T>(resultSet, returnTypeClass, cal, mapValType, mapKeyType));
|
||||||
@ -27,4 +30,16 @@ public class CleaningRowToObjectMapper<K, T> implements RowMapper<K, T> {
|
|||||||
public K getMapKey() throws SQLException {
|
public K getMapKey() throws SQLException {
|
||||||
return delegate.getMapKey();
|
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<T> getResultSetToObject() {
|
||||||
|
if(this.delegateRsto == null)
|
||||||
|
this.delegateRsto = delegate.getResultSetToObject();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package com.moparisthebest.jdbc;
|
|||||||
|
|
||||||
import com.moparisthebest.classgen.Compiler;
|
import com.moparisthebest.classgen.Compiler;
|
||||||
|
|
||||||
import javax.lang.model.type.TypeMirror;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.AccessibleObject;
|
import java.lang.reflect.AccessibleObject;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -83,6 +82,16 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
|||||||
return resultSetToObject.getFirstColumn(_resultSet, _cal);
|
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<T> getResultSetToObject() {
|
||||||
|
return resultSetToObject;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getKeysFromResultSet() throws SQLException {
|
protected String[] getKeysFromResultSet() throws SQLException {
|
||||||
if (keys == null)
|
if (keys == null)
|
||||||
@ -95,7 +104,7 @@ public class CompilingRowToObjectMapper<K, T> extends RowToObjectMapper<K, T> {
|
|||||||
throw new MapperException("not supported here");
|
throw new MapperException("not supported here");
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ResultSetToObject<K, T> {
|
public interface ResultSetToObject<K, T> extends com.moparisthebest.jdbc.util.ResultSetToObject<T> {
|
||||||
K getFirstColumn(final ResultSet rs, final Calendar cal) throws SQLException;
|
K getFirstColumn(final ResultSet rs, final Calendar cal) throws SQLException;
|
||||||
T toObject(final ResultSet rs, final Calendar cal) throws SQLException;
|
T toObject(final ResultSet rs, final Calendar cal) throws SQLException;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
@ -240,6 +242,26 @@ public class ListQueryMapper extends QueryMapper {
|
|||||||
return delegate.toObject(prepareSql(sql, bindObjects), componentType, bindObjects);
|
return delegate.toObject(prepareSql(sql, bindObjects), componentType, bindObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(PreparedStatement ps, Class<T> componentType, final Object... bindObjects) throws SQLException {
|
||||||
|
return delegate.toResultSetIterable(ps, componentType, bindObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(String sql, Class<T> componentType, final Object... bindObjects) throws SQLException {
|
||||||
|
return delegate.toResultSetIterable(prepareSql(sql, bindObjects), componentType, bindObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(PreparedStatement ps, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
|
return delegate.toResultSetIterableMap(ps, componentType, mapValType, bindObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(String sql, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
|
return delegate.toResultSetIterableMap(prepareSql(sql, bindObjects), componentType, mapValType, bindObjects);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(PreparedStatement ps, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(PreparedStatement ps, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
return delegate.toSingleMap(ps, componentType, mapValType, bindObjects);
|
return delegate.toSingleMap(ps, componentType, mapValType, bindObjects);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
@ -257,6 +259,46 @@ public class NullQueryMapper extends QueryMapper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(PreparedStatement query, Class<T> componentType, final Object... bindObjects) {
|
||||||
|
try {
|
||||||
|
return delegate.toResultSetIterable(query, componentType, bindObjects);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
if (verbose) e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(String query, Class<T> componentType, final Object... bindObjects) {
|
||||||
|
try {
|
||||||
|
return delegate.toResultSetIterable(query, componentType, bindObjects);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
if (verbose) e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(PreparedStatement query, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) {
|
||||||
|
try {
|
||||||
|
return delegate.toResultSetIterableMap(query, componentType, mapValType, bindObjects);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
if (verbose) e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(String query, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) {
|
||||||
|
try {
|
||||||
|
return delegate.toResultSetIterableMap(query, componentType, mapValType, bindObjects);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
if (verbose) e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(PreparedStatement query, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) {
|
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(PreparedStatement query, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||||
|
|
||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
import javax.naming.InitialContext;
|
import javax.naming.InitialContext;
|
||||||
import javax.sql.DataSource;
|
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 <T> ResultSetIterable<T> toResultSetIterable(String sql, Class<T> componentType, final Object... bindObjects) throws SQLException {
|
||||||
|
boolean error = true;
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
ResultSetIterable<T> 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 <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(String sql, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
|
boolean error = true;
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
ResultSetIterable<Map<String, V>> 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
|
// these are standard getters
|
||||||
|
|
||||||
public ResultSetMapper getCustomResultSetMapper() {
|
public ResultSetMapper getCustomResultSetMapper() {
|
||||||
@ -364,6 +408,14 @@ public class QueryMapper implements Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(PreparedStatement ps, Class<T> componentType, final Object... bindObjects) throws SQLException {
|
||||||
|
return cm.toResultSetIterable(bindExecute(ps, bindObjects), componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(PreparedStatement ps, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
|
return cm.toResultSetIterableMap(bindExecute(ps, bindObjects), componentType, mapValType);
|
||||||
|
}
|
||||||
|
|
||||||
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(PreparedStatement ps, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(PreparedStatement ps, Class<T> componentType, Class<V> mapValType, final Object... bindObjects) throws SQLException {
|
||||||
return cm.toSingleMap(bindExecute(ps, bindObjects), componentType, mapValType);
|
return cm.toSingleMap(bindExecute(ps, bindObjects), componentType, mapValType);
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,11 @@ package com.moparisthebest.jdbc;
|
|||||||
* $Header:$
|
* $Header:$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||||
|
|
||||||
import java.lang.ref.SoftReference;
|
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.ResultSet;
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@ -122,12 +125,22 @@ public class ResultSetMapper implements RowMapperProvider {
|
|||||||
if (rs.next())
|
if (rs.next())
|
||||||
ret = getRowMapper(rs, componentType, cal, mapValType, null).mapRowToReturnType();
|
ret = getRowMapper(rs, componentType, cal, mapValType, null).mapRowToReturnType();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
// ignore
|
// ignore // todo: this looks crazy dangerous look into it...
|
||||||
}
|
}
|
||||||
tryClose(rs);
|
tryClose(rs);
|
||||||
return ret == null ? RowToObjectMapper.fixNull(componentType) : ret;
|
return ret == null ? RowToObjectMapper.fixNull(componentType) : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected <T> ResultSetIterable<T> privToResultSetIterable(ResultSet rs, Class<T> 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 <T extends Collection<E>, E> T privToCollection(ResultSet rs, final Class<T> collectionType, Class<E> componentType, int arrayMaxLength, Calendar cal, Class<?> mapValType) {
|
protected <T extends Collection<E>, E> T privToCollection(ResultSet rs, final Class<T> collectionType, Class<E> componentType, int arrayMaxLength, Calendar cal, Class<?> mapValType) {
|
||||||
return privToCollection(rs, instantiateClass(collectionType, ArrayList.class), componentType, arrayMaxLength, cal, 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);
|
return privToObject(rs, componentType, cal, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(ResultSet rs, Class<T> componentType, Calendar cal) {
|
||||||
|
return privToResultSetIterable(rs, componentType, cal, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(ResultSet rs, Class<T> componentType, Class<V> mapValType, Calendar cal) {
|
||||||
|
return (ResultSetIterable<Map<String, V>>)privToResultSetIterable(rs, componentType, cal, mapValType);
|
||||||
|
}
|
||||||
|
|
||||||
public <T extends Collection<E>, E> T toCollection(ResultSet rs, final Class<T> collectionType, Class<E> componentType, int arrayMaxLength, Calendar cal) {
|
public <T extends Collection<E>, E> T toCollection(ResultSet rs, final Class<T> collectionType, Class<E> componentType, int arrayMaxLength, Calendar cal) {
|
||||||
return privToCollection(rs, collectionType, componentType, arrayMaxLength, cal, null);
|
return privToCollection(rs, collectionType, componentType, arrayMaxLength, cal, null);
|
||||||
}
|
}
|
||||||
@ -607,6 +629,14 @@ public class ResultSetMapper implements RowMapperProvider {
|
|||||||
return this.toObject(rs, componentType, cal);
|
return this.toObject(rs, componentType, cal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> ResultSetIterable<T> toResultSetIterable(ResultSet rs, Class<T> componentType) {
|
||||||
|
return this.toResultSetIterable(rs, componentType, cal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Map<String, V>, V> ResultSetIterable<Map<String, V>> toResultSetIterableMap(ResultSet rs, Class<T> componentType, Class<V> mapValType) {
|
||||||
|
return this.toResultSetIterableMap(rs, componentType, mapValType, cal);
|
||||||
|
}
|
||||||
|
|
||||||
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(ResultSet rs, Class<T> componentType, Class<V> mapValType) {
|
public <T extends Map<String, V>, V> Map<String, V> toSingleMap(ResultSet rs, Class<T> componentType, Class<V> mapValType) {
|
||||||
return this.toSingleMap(rs, componentType, mapValType, cal);
|
return this.toSingleMap(rs, componentType, mapValType, cal);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetToObject;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,4 +20,6 @@ public interface RowMapper<K,T> {
|
|||||||
* @throws MapperException if _mapKeyType is null
|
* @throws MapperException if _mapKeyType is null
|
||||||
*/
|
*/
|
||||||
K getMapKey() throws SQLException;
|
K getMapKey() throws SQLException;
|
||||||
|
|
||||||
|
ResultSetToObject<T> getResultSetToObject();
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
package com.moparisthebest.jdbc;
|
package com.moparisthebest.jdbc;
|
||||||
|
|
||||||
import com.moparisthebest.jdbc.dto.*;
|
import com.moparisthebest.jdbc.dto.*;
|
||||||
|
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static com.moparisthebest.jdbc.TryClose.tryClose;
|
import static com.moparisthebest.jdbc.TryClose.tryClose;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
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 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 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 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 Person setPerson1 = new SetPerson(fieldPerson1);
|
||||||
public static final Boss setBoss1 = new SetBoss(fieldBoss1);
|
public static final Boss setBoss1 = new SetBoss(fieldBoss1);
|
||||||
@ -70,9 +78,9 @@ public class QueryMapperTest {
|
|||||||
qm = new QueryMapper(conn);
|
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 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))");
|
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());
|
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 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());
|
qm.executeUpdate("INSERT INTO boss (person_no, department) VALUES (?, ?)", boss.getPersonNo(), boss.getDepartment());
|
||||||
}
|
}
|
||||||
@ -249,7 +257,7 @@ public class QueryMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testSelectArrayMap() throws Throwable {
|
public void testSelectArrayMap() throws Throwable {
|
||||||
final List<Map<String, String>> arrayMap = getListMap();
|
final List<Map<String, String>> 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
|
@Test
|
||||||
@ -304,19 +312,19 @@ public class QueryMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testSelectLongObjectArray() throws Throwable {
|
public void testSelectLongObjectArray() throws Throwable {
|
||||||
final Long[] expected = {fieldPerson1.getPersonNo()};
|
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
|
@Test
|
||||||
public void testSelectObjectArray() throws Throwable {
|
public void testSelectObjectArray() throws Throwable {
|
||||||
final Long[] arr = {1L, 2L, 3L};
|
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
|
@Test
|
||||||
public void testSelectPrimitiveArray() throws Throwable {
|
public void testSelectPrimitiveArray() throws Throwable {
|
||||||
final long[] arr = {1L, 2L, 3L};
|
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)
|
@Test(expected = com.moparisthebest.jdbc.MapperException.class)
|
||||||
@ -371,4 +379,31 @@ public class QueryMapperTest {
|
|||||||
assertNull(map.get("TOM"));
|
assertNull(map.get("TOM"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testList() throws SQLException {
|
||||||
|
final List<FieldPerson> 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<FieldPerson> 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<FieldPerson> 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<FieldPerson> fromDb = new ArrayList<FieldPerson>();
|
||||||
|
for(final FieldPerson fieldPerson : rsi)
|
||||||
|
fromDb.add(fieldPerson);
|
||||||
|
rsi.close();
|
||||||
|
assertArrayEquals(people, fromDb.toArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user