mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-12-21 23:08:52 -05:00
Add TypeReference and toType method to querymapper
This commit is contained in:
parent
c1b8cfcf3b
commit
195a0c0484
@ -82,7 +82,7 @@ do
|
||||
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" != 'toStream(' ]; then
|
||||
if [ "$method_name" != 'toResultSetIterable(' -a "$method_name" != 'toStream(' -a "$method_name" != 'toType(' ]; then
|
||||
|
||||
cat >> "$query" <<EOF
|
||||
$(echo $method | sed -e 's/ResultSet rs/String sql/' -e 's/) {/, final Object... bindObjects) throws SQLException {/')
|
||||
@ -97,7 +97,7 @@ EOF
|
||||
|
||||
EOF
|
||||
|
||||
fi # end special case toResultSetIterable/toStream
|
||||
fi # end special case toResultSetIterable/toStream/toType
|
||||
|
||||
# CachingQueryMapper.java
|
||||
cat >> "$caching_query" <<EOF
|
||||
|
@ -180,6 +180,11 @@ public class CachingQueryMapper extends QueryMapper {
|
||||
return super.toSingleMap(getPreparedStatement(sql), mapValType, bindObjects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toType(String sql, TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
|
||||
return super.toType(getPreparedStatement(sql), typeReference, bindObjects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Collection<E>, E> T toCollection(String sql, final Class<T> collectionType, Class<E> componentType, final Object... bindObjects) throws SQLException {
|
||||
return super.toCollection(getPreparedStatement(sql), collectionType, componentType, bindObjects);
|
||||
|
@ -313,6 +313,16 @@ public class ListQueryMapper extends QueryMapper {
|
||||
return delegate.toSingleMap(prepareSql(sql, bindObjects), mapValType, bindObjects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toType(PreparedStatement ps, TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
|
||||
return delegate.toType(ps, typeReference, bindObjects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toType(String sql, TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
|
||||
return delegate.toType(prepareSql(sql, bindObjects), typeReference, bindObjects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Collection<E>, E> T toCollection(PreparedStatement ps, final Class<T> collectionType, Class<E> componentType, final Object... bindObjects) throws SQLException {
|
||||
return delegate.toCollection(ps, collectionType, componentType, bindObjects);
|
||||
|
@ -390,6 +390,26 @@ public class NullQueryMapper extends QueryMapper {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toType(PreparedStatement query, TypeReference<T> typeReference, final Object... bindObjects) {
|
||||
try {
|
||||
return delegate.toType(query, typeReference, bindObjects);
|
||||
} catch (Throwable e) {
|
||||
if (verbose) e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T toType(String query, TypeReference<T> typeReference, final Object... bindObjects) {
|
||||
try {
|
||||
return delegate.toType(query, typeReference, bindObjects);
|
||||
} catch (Throwable e) {
|
||||
if (verbose) e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Collection<E>, E> T toCollection(PreparedStatement query, final Class<T> collectionType, Class<E> componentType, final Object... bindObjects) {
|
||||
try {
|
||||
|
@ -345,6 +345,52 @@ 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
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T toType(String sql, TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
|
||||
boolean error = true, closePs = true;
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
T ret = null;
|
||||
try {
|
||||
ps = conn.prepareStatement(sql);
|
||||
rs = this.toResultSet(ps, bindObjects);
|
||||
ret = cm.toType(rs, typeReference);
|
||||
if(ret instanceof ResultSetIterable) {
|
||||
ret = (T)((ResultSetIterable)ret).setPreparedStatementToClose(ps);
|
||||
closePs = false;
|
||||
}
|
||||
//IFJAVA8_START
|
||||
else if(ret instanceof Stream) {
|
||||
final PreparedStatement finalPs = ps;
|
||||
ret = (T)((Stream)ret).onClose(() -> tryClose(finalPs));
|
||||
closePs = false;
|
||||
}
|
||||
//IFJAVA8_END
|
||||
else if(ret instanceof ResultSet) {
|
||||
ret = (T)new StatementClosingResultSet(rs, ps);
|
||||
closePs = false;
|
||||
}
|
||||
error = false;
|
||||
return ret;
|
||||
} finally {
|
||||
if (error) {
|
||||
if(ret != null) {
|
||||
if(ret instanceof ResultSet)
|
||||
tryClose((ResultSet)ret);
|
||||
else if(ret instanceof Closeable)
|
||||
tryClose((Closeable)ret);
|
||||
//IFJAVA8_START
|
||||
else if(ret instanceof AutoCloseable)
|
||||
tryClose((AutoCloseable)ret);
|
||||
//IFJAVA8_END
|
||||
}
|
||||
tryClose(rs);
|
||||
tryClose(ps);
|
||||
} else if(closePs)
|
||||
tryClose(ps);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> ResultSetIterable<T> toResultSetIterable(String sql, Class<T> componentType, final Object... bindObjects) throws SQLException {
|
||||
boolean error = true;
|
||||
PreparedStatement ps = null;
|
||||
@ -507,6 +553,10 @@ public class QueryMapper implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T toType(PreparedStatement ps, TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
|
||||
return cm.toType(bindExecute(ps, bindObjects), typeReference);
|
||||
}
|
||||
|
||||
public <T extends Collection<E>, E> T toCollection(PreparedStatement ps, final Class<T> collectionType, Class<E> componentType, final Object... bindObjects) throws SQLException {
|
||||
return cm.toCollection(bindExecute(ps, bindObjects), collectionType, componentType);
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
@ -463,6 +465,45 @@ public class ResultSetMapper implements RowMapperProvider {
|
||||
|
||||
// overloaded helper methods
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object toType(final ResultSet rs, final Class returnType, final ParameterizedType type, final int arrayMaxLength, final Calendar cal) {
|
||||
if (returnType.isArray()) {
|
||||
return toArray(rs, returnType.getComponentType(), arrayMaxLength, cal);
|
||||
} else if (Collection.class.isAssignableFrom(returnType)) {
|
||||
return toCollection(rs, returnType, (Class) type.getActualTypeArguments()[0], arrayMaxLength, cal);
|
||||
} else if (Map.class.isAssignableFrom(returnType)) {
|
||||
Type[] types = type.getActualTypeArguments();
|
||||
if (types[1] instanceof ParameterizedType) { // for collectionMaps
|
||||
ParameterizedType pt = (ParameterizedType) types[1];
|
||||
Class collectionType = (Class) pt.getRawType();
|
||||
if (Collection.class.isAssignableFrom(collectionType))
|
||||
return toMapCollection(rs, returnType, (Class) types[0], collectionType, (Class) pt.getActualTypeArguments()[0], arrayMaxLength, cal);
|
||||
}
|
||||
return toMap(rs, com.moparisthebest.jdbc.ResultSetMapper.instantiateClass((Class<Map>)returnType, HashMap.class), (Class) types[0], (Class) types[1], arrayMaxLength, cal);
|
||||
} else if (Iterator.class.isAssignableFrom(returnType)) {
|
||||
if(ResultSetIterable.class.isAssignableFrom(returnType))
|
||||
return toResultSetIterable(rs, (Class) type.getActualTypeArguments()[0], cal);
|
||||
return ListIterator.class.isAssignableFrom(returnType) ?
|
||||
toListIterator(rs, (Class) type.getActualTypeArguments()[0], arrayMaxLength, cal) :
|
||||
toIterator(rs, (Class) type.getActualTypeArguments()[0], arrayMaxLength, cal);
|
||||
}
|
||||
//IFJAVA8_START
|
||||
else if (Stream.class.isAssignableFrom(returnType)) {
|
||||
return toStream(rs, (Class) type.getActualTypeArguments()[0], cal);
|
||||
}
|
||||
//IFJAVA8_END
|
||||
else if(ResultSet.class.isAssignableFrom(returnType)) {
|
||||
return rs; // odd, we didn't do much, but oh well
|
||||
} else {
|
||||
return toObject(rs, returnType, cal);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T toType(ResultSet rs, TypeReference<T> typeReference, int arrayMaxLength, Calendar cal) {
|
||||
return (T)this.toType(rs, typeReference.getRawType(), typeReference.getType(), arrayMaxLength, cal);
|
||||
}
|
||||
|
||||
public <T> T toObject(ResultSet rs, Class<T> componentType, Calendar cal) {
|
||||
return privToObject(rs, componentType, cal, null);
|
||||
}
|
||||
@ -691,6 +732,18 @@ public class ResultSetMapper implements RowMapperProvider {
|
||||
return this.toSingleMap(rs, mapValType, cal);
|
||||
}
|
||||
|
||||
public <T> T toType(ResultSet rs, TypeReference<T> typeReference) {
|
||||
return this.toType(rs, typeReference, arrayMaxLength, cal);
|
||||
}
|
||||
|
||||
public <T> T toType(ResultSet rs, TypeReference<T> typeReference, int arrayMaxLength) {
|
||||
return this.toType(rs, typeReference, arrayMaxLength, cal);
|
||||
}
|
||||
|
||||
public <T> T toType(ResultSet rs, TypeReference<T> typeReference, Calendar cal) {
|
||||
return this.toType(rs, typeReference, arrayMaxLength, cal);
|
||||
}
|
||||
|
||||
public <T extends Collection<E>, E> T toCollection(ResultSet rs, final Class<T> collectionType, Class<E> componentType) {
|
||||
return this.toCollection(rs, collectionType, componentType, arrayMaxLength, cal);
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
package com.moparisthebest.jdbc;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This generic abstract class is used for obtaining full generics type information
|
||||
* by sub-classing.
|
||||
* <p>
|
||||
* Class is based on ideas from
|
||||
* <a href="http://gafter.blogspot.com/2006/12/super-type-tokens.html"
|
||||
* >http://gafter.blogspot.com/2006/12/super-type-tokens.html</a>,
|
||||
* Additional idea (from a suggestion made in comments of the article)
|
||||
* is to require bogus implementation of <code>Comparable</code>
|
||||
* (any such generic interface would do, as long as it forces a method
|
||||
* with generic type to be implemented).
|
||||
* to ensure that a Type argument is indeed given.
|
||||
* <p>
|
||||
* Usage is by sub-classing: here is one way to instantiate reference
|
||||
* to generic type <code>List<Integer></code>:
|
||||
* <pre>
|
||||
* TypeReference ref = new TypeReference<List<Integer>>() { };
|
||||
* </pre>
|
||||
* which can be passed to methods that accept TypeReference
|
||||
* <p>
|
||||
* Pulled from jackson here:
|
||||
* https://raw.githubusercontent.com/FasterXML/jackson-core/3dcedd2b6838ef29abb179557b6e42479e93834b/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java
|
||||
* Thankfully jackson has an identical Apache 2.0 license so no issues there
|
||||
*/
|
||||
public abstract class TypeReference<T> implements Comparable<TypeReference<T>> {
|
||||
|
||||
private final ParameterizedType type;
|
||||
private final Class<?> rawType;
|
||||
|
||||
protected TypeReference() {
|
||||
final Type superClass = getClass().getGenericSuperclass();
|
||||
if (superClass instanceof Class<?>) { // sanity check, should never happen
|
||||
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
|
||||
}
|
||||
/* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect
|
||||
* it is possible to make it fail?
|
||||
* But let's deal with specific
|
||||
* case when we know an actual use case, and thereby suitable
|
||||
* workarounds for valid case(s) and/or error to throw
|
||||
* on invalid one(s).
|
||||
*/
|
||||
final Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
|
||||
if (type instanceof Class<?>) {
|
||||
this.type = null;
|
||||
this.rawType = (Class<?>) type;
|
||||
} else if (type instanceof ParameterizedType) {
|
||||
this.type = (ParameterizedType) type;
|
||||
this.rawType = (Class<?>) this.type.getRawType();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Internal error: TypeReference constructed with unknown type: '" + type + "' class: '" + type.getClass() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
public final ParameterizedType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public final Class<?> getRawType() {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The only reason we define this method (and require implementation
|
||||
* of <code>Comparable</code>) is to prevent constructing a
|
||||
* reference without type information.
|
||||
*/
|
||||
@Override
|
||||
public final int compareTo(TypeReference<T> o) {
|
||||
return 0;
|
||||
}
|
||||
// just need an implementation, not a good one... hence ^^^
|
||||
}
|
||||
|
@ -402,6 +402,13 @@ public class QueryMapperTest {
|
||||
assertArrayEquals(people, fromDb.toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListType() throws SQLException {
|
||||
final List<FieldPerson> fromDb = qm.toType("SELECT * from person WHERE person_no IN (?,?,?) ORDER BY person_no",
|
||||
new TypeReference<List<FieldPerson>>() {}, 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);
|
||||
|
Loading…
Reference in New Issue
Block a user