diff --git a/beehive-jdbc-mapper/genQueryMapper.sh b/beehive-jdbc-mapper/genQueryMapper.sh index 2ec05a6..58d5800 100755 --- a/beehive-jdbc-mapper/genQueryMapper.sh +++ b/beehive-jdbc-mapper/genQueryMapper.sh @@ -59,6 +59,7 @@ finishFile "src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java" query="$(prepareFile "src/main/java/com/moparisthebest/jdbc/QueryMapper.java")" caching_query="$(prepareFile "src/main/java/com/moparisthebest/jdbc/CachingQueryMapper.java")" null_query="$(prepareFile "src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java")" +list_query="$(prepareFile "src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java")" cat src/main/java/com/moparisthebest/jdbc/ResultSetMapper.java | grep public | grep '(ResultSet rs' | egrep -v '(int arrayMaxLength|Calendar cal)' | while read method do @@ -110,8 +111,23 @@ EOF EOF done >> "$null_query" + + # ListQueryMapper.java + cat >> "$list_query" < Array toArray(final Connection conn, final Collection values) throws SQLException { + return conn.createArrayOf( + values.iterator().next() instanceof Number ? "number" : "text", + values.toArray() + ); + } + + public InListObject inList(final Connection conn, final String columnName, final Collection values) throws SQLException { + return values == null || values.isEmpty() ? InListObject.empty : new ArrayListObject( + columnName + columnAppend(), + toArray(conn, values) + ); + } + + class ArrayListObject extends InListObject { + private final Array array; + + public ArrayListObject(final String sql, final Array array) { + super(sql); + this.array = array; + } + + public Array getArray() { + return array; + } + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/BindInList.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/BindInList.java new file mode 100644 index 0000000..80f4288 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/BindInList.java @@ -0,0 +1,92 @@ +package com.moparisthebest.jdbc; + +import java.sql.Connection; +import java.util.*; + +/** + * Created by mopar on 4/29/15. + */ +public class BindInList implements InList { + + private static final int defaultMaxSize = Integer.parseInt(System.getProperty("QueryMapper.BindInList.defaultMaxSize", "999")); + + private static final InList instance = new BindInList(); + + public static InList instance() { + return instance; + } + + private final int maxSize; + + public BindInList(final int maxSize) { + this.maxSize = maxSize; + } + + protected BindInList() { + this(defaultMaxSize); + } + + private static List> split(final List list, final int maxLength) { + final int listSize = list.size(); + final List> ret = new ArrayList>(); + if (listSize < maxLength) + ret.add(list); + else + for (int fromIndex = 0, toIndex = maxLength; fromIndex < listSize; fromIndex = toIndex, toIndex += maxLength) + ret.add(list.subList(fromIndex, toIndex > listSize ? listSize : toIndex)); + return ret; + } + + private String toInList(final String fieldName, final Collection items) { + final StringBuilder sb = new StringBuilder("("); + + // do it quick if it will fit in one in-list + if (items.size() < maxSize) // 999 or less + return buildInList(items, sb, fieldName).append(")").toString(); + + // else we need to split lists + boolean notFirst = false; + for (final List item : split(new ArrayList(items), maxSize)) { + if (notFirst) sb.append(" OR "); + else notFirst = true; + buildInList(item, sb, fieldName); + } + + return sb.append(")").toString(); + } + + private static StringBuilder buildInList(Iterable list, StringBuilder sb, String fieldName) { + return oracleCommaSeparatedList(list, sb.append(fieldName).append(" IN (")).append(")"); + } + + private static StringBuilder oracleCommaSeparatedList(Iterable list, StringBuilder sb) { + boolean notFirst = false; + for (final T obj : list) { + // DO NOT DO THIS ANYMORE: if (obj == null) continue; + if (notFirst) sb.append(','); + else notFirst = true; + sb.append('?'); + } + return sb; + } + + public InListObject inList(final Connection conn, final String columnName, final Collection values) { + return values == null || values.isEmpty() ? InListObject.empty : new BindInListObject( + toInList(columnName, values), + values.toArray() + ); + } + + class BindInListObject extends InListObject { + private final Object[] bindObjects; + + public BindInListObject(final String sql, final Object[] bindObjects) { + super(sql); + this.bindObjects = bindObjects; + } + + public Object[] getBindObjects() { + return bindObjects; + } + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/InList.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/InList.java new file mode 100644 index 0000000..16e9ab7 --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/InList.java @@ -0,0 +1,35 @@ +package com.moparisthebest.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collection; + +/** + * For a column name and a Collection, return a Object usable by QueryMapper for binding to a PreparedStatement and + * ListQueryMapper for substituting in the query + */ +public interface InList { + + /** + * Returns an Object who's .toString returns a String for a query, and QueryMapper knows how to bind to a PreparedStatement + * @param columnName Column name for query + * @param values values for in list + * @return object + */ + public InListObject inList(final Connection conn, final String columnName, final Collection values) throws SQLException; + + class InListObject { + static final InListObject empty = new InListObject("(0=1)"); + + private final String sql; + + public InListObject(final String sql) { + this.sql = sql; + } + + @Override + public final String toString() { + return sql; + } + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java new file mode 100644 index 0000000..93ded3c --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/ListQueryMapper.java @@ -0,0 +1,554 @@ +package com.moparisthebest.jdbc; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +public class ListQueryMapper extends QueryMapper { + + private static final InList defaultInList; + + static { + InList def = null; + try { + final Class ensureContext = Class.forName(System.getProperty("QueryMapper.defaultInList.class", "com.moparisthebest.jdbc.BindInList")); + final Method method = ensureContext.getMethod(System.getProperty("QueryMapper.defaultInList.method", "instance")); + def = (InList) method.invoke(null); + } catch (Throwable e) { + // NEVER ignore + throw new RuntimeException(e); + } + defaultInList = def; + } + + protected final QueryMapper delegate; + protected final InList inList; + + private static final String inListReplace = "{inList}"; + + private ListQueryMapper(Connection conn, String jndiName, QueryMapper delegate, ResultSetMapper cm, InList inList) { + this.inList = inList; + this.delegate = delegate == null ? new QueryMapper(conn, jndiName, cm) : + (delegate instanceof ListQueryMapper ? ((ListQueryMapper)delegate).delegate : delegate); + } + + public ListQueryMapper(QueryMapper delegate, InList inList) { + this(null, null, delegate, null, inList); + } + + public ListQueryMapper(QueryMapper delegate) { + this(null, null, delegate, null, defaultInList); + } + + public ListQueryMapper(Connection conn, InList inList) { + this(conn, null, null, null, inList); + } + + public ListQueryMapper(Connection conn, ResultSetMapper cm, InList inList) { + this(conn, null, null, cm, inList); + } + + public ListQueryMapper(Connection conn) { + this(conn, defaultInList); + } + + public ListQueryMapper(Connection conn, ResultSetMapper cm) { + this(conn, cm, defaultInList); + } + + public ListQueryMapper(String jndiName, InList inList) { + this(null, jndiName, null, null, inList); + } + + public ListQueryMapper(String jndiName, ResultSetMapper cm, InList inList) { + this(null, jndiName, null, cm, inList); + } + + public ListQueryMapper(String jndiName) { + this(jndiName, defaultInList); + } + + public ListQueryMapper(String jndiName, ResultSetMapper cm) { + this(jndiName, cm, defaultInList); + } + + public static ListQueryMapper wrap(final QueryMapper qm){ + return qm instanceof ListQueryMapper ? (ListQueryMapper)qm : new ListQueryMapper(qm); + } + + public static ListQueryMapper wrap(final QueryMapper qm, final InList inList){ + return qm instanceof ListQueryMapper && ((ListQueryMapper)qm).inList == inList ? (ListQueryMapper)qm : new ListQueryMapper(qm, inList); + } + + public InList.InListObject inList(final String columnName, final Collection values) throws SQLException { + return this.inList.inList(delegate.conn, columnName, values); + } + + // these update the database + + @Override + public int executeUpdate(final PreparedStatement ps, final Object... bindObjects) throws SQLException { + return delegate.executeUpdate(ps, bindObjects); + } + + @Override + public boolean executeUpdateSuccess(final PreparedStatement ps, final Object... bindObjects) throws SQLException { + return delegate.executeUpdateSuccess(ps, bindObjects); + } + + // these update the database using UpdateableDTOs + + @Override + public int updateRows(final UpdateableDTO dto) throws SQLException { + return delegate.updateRows(dto); + } + + @Override + public int updateRows(final Collection dtos) throws SQLException { + return delegate.updateRows(dtos); + } + + @Override + public int updateRows(final UpdateableDTO[] dtos) throws SQLException { + return delegate.updateRows(dtos); + } + + @Override + public int insertRows(final UpdateableDTO dto) throws SQLException { + return delegate.insertRows(dto); + } + + @Override + public int insertRows(final Collection dtos) throws SQLException { + return delegate.insertRows(dtos); + } + + @Override + public int insertRows(final UpdateableDTO[] dtos) throws SQLException { + return delegate.insertRows(dtos); + } + + // these grab ResultSets from the database + + @Override + public ResultSet toResultSet(PreparedStatement ps, Object... bindObjects) throws SQLException { + return delegate.toResultSet(ps, bindObjects); + } + + // these are standard getters + + @Override + public ResultSetMapper getCustomResultSetMapper() { + return delegate.getCustomResultSetMapper(); + } + + @Override + public Connection getConnection() { + return delegate.getConnection(); + } + + // these just delegate and change no functionality + + @Override + public void close() { + delegate.close(); + } + + // and these are standard + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final ListQueryMapper that = (ListQueryMapper) o; + + if (delegate != null ? !delegate.equals(that.delegate) : that.delegate != null) return false; + if (inList != null ? !inList.equals(that.inList) : that.inList != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = delegate != null ? delegate.hashCode() : 0; + result = 31 * result + (inList != null ? inList.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "ListQueryMapper{" + + "delegate=" + delegate + + ", inList=" + inList + + "} " + super.toString(); + } + + // begin of ListQueryMapper specific methods + + private static StringBuilder recursiveReplace(final StringBuilder sb, final Object... bindObjects) { + if (bindObjects != null && bindObjects.length > 0) { + for (Object o : bindObjects) { + if (o != null) { + if (o instanceof InList.InListObject) { + final int startIndex = sb.indexOf(inListReplace); + if (startIndex < -1) + return sb; // we've replaced all, maybe an error? meh + sb.replace(startIndex, startIndex + inListReplace.length(), o.toString()); + } else if (o instanceof Object[]) { + recursiveReplace(sb, (Object[]) o); + } else if (o instanceof Collection) { + recursiveReplace(sb, ((Collection) o).toArray()); + } + } + } + } + return sb; + } + + protected String prepareSql(final String sql, final Object... bindObjects) { + return !sql.contains(inListReplace) ? sql : recursiveReplace(new StringBuilder(sql), bindObjects).toString(); + } + + @Override + public int executeUpdate(final String sql, final Object... bindObjects) throws SQLException { + return delegate.executeUpdate(prepareSql(sql, bindObjects), bindObjects); + } + + @Override + public boolean executeUpdateSuccess(final String sql, final Object... bindObjects) throws SQLException { + return delegate.executeUpdateSuccess(prepareSql(sql, bindObjects), bindObjects); + } + + @Override + public ResultSet toResultSet(String sql, Object... bindObjects) throws SQLException { + return delegate.toResultSet(prepareSql(sql, bindObjects), bindObjects); + } + + // DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh + + @Override + public T toObject(PreparedStatement ps, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toObject(ps, componentType, bindObjects); + } + + @Override + public T toObject(String sql, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toObject(prepareSql(sql, bindObjects), componentType, bindObjects); + } + + @Override + public , V> Map toSingleMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toSingleMap(ps, componentType, mapValType, bindObjects); + } + + @Override + public , V> Map toSingleMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toSingleMap(prepareSql(sql, bindObjects), componentType, mapValType, bindObjects); + } + + @Override + public Map toSingleMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toSingleMap(ps, mapValType, bindObjects); + } + + @Override + public Map toSingleMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toSingleMap(prepareSql(sql, bindObjects), mapValType, bindObjects); + } + + @Override + public , E> T toCollection(PreparedStatement ps, final Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toCollection(ps, collectionType, componentType, bindObjects); + } + + @Override + public , E> T toCollection(String sql, final Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toCollection(prepareSql(sql, bindObjects), collectionType, componentType, bindObjects); + } + + @Override + public , E> T toCollection(PreparedStatement ps, T list, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toCollection(ps, list, componentType, bindObjects); + } + + @Override + public , E> T toCollection(String sql, T list, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toCollection(prepareSql(sql, bindObjects), list, componentType, bindObjects); + } + + @Override + public , K, E> T toMap(PreparedStatement ps, final Class returnType, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMap(ps, returnType, mapKeyType, componentType, bindObjects); + } + + @Override + public , K, E> T toMap(String sql, final Class returnType, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMap(prepareSql(sql, bindObjects), returnType, mapKeyType, componentType, bindObjects); + } + + @Override + public , K, E> T toMap(PreparedStatement ps, T map, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMap(ps, map, mapKeyType, componentType, bindObjects); + } + + @Override + public , K, E> T toMap(String sql, T map, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMap(prepareSql(sql, bindObjects), map, mapKeyType, componentType, bindObjects); + } + + @Override + public , K, E extends Collection, C> T toMapCollection(PreparedStatement ps, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollection(ps, returnType, mapKeyType, collectionType, componentType, bindObjects); + } + + @Override + public , K, E extends Collection, C> T toMapCollection(String sql, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollection(prepareSql(sql, bindObjects), returnType, mapKeyType, collectionType, componentType, bindObjects); + } + + @Override + public , K, E extends Collection, C> T toMapCollection(PreparedStatement ps, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollection(ps, map, mapKeyType, collectionType, componentType, bindObjects); + } + + @Override + public , K, E extends Collection, C> T toMapCollection(String sql, T map, Class mapKeyType, Class collectionType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollection(prepareSql(sql, bindObjects), map, mapKeyType, collectionType, componentType, bindObjects); + } + + @Override + public ListIterator toListIterator(PreparedStatement ps, final Class type, final Object... bindObjects) throws SQLException { + return delegate.toListIterator(ps, type, bindObjects); + } + + @Override + public ListIterator toListIterator(String sql, final Class type, final Object... bindObjects) throws SQLException { + return delegate.toListIterator(prepareSql(sql, bindObjects), type, bindObjects); + } + + @Override + public Iterator toIterator(PreparedStatement ps, final Class type, final Object... bindObjects) throws SQLException { + return delegate.toIterator(ps, type, bindObjects); + } + + @Override + public Iterator toIterator(String sql, final Class type, final Object... bindObjects) throws SQLException { + return delegate.toIterator(prepareSql(sql, bindObjects), type, bindObjects); + } + + @Override + public T[] toArray(PreparedStatement ps, final Class type, final Object... bindObjects) throws SQLException { + return delegate.toArray(ps, type, bindObjects); + } + + @Override + public T[] toArray(String sql, final Class type, final Object... bindObjects) throws SQLException { + return delegate.toArray(prepareSql(sql, bindObjects), type, bindObjects); + } + + @Override + public List toList(PreparedStatement ps, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toList(ps, componentType, bindObjects); + } + + @Override + public List toList(String sql, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toList(prepareSql(sql, bindObjects), componentType, bindObjects); + } + + @Override + public Map toMap(PreparedStatement ps, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMap(ps, mapKeyType, componentType, bindObjects); + } + + @Override + public Map toMap(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMap(prepareSql(sql, bindObjects), mapKeyType, componentType, bindObjects); + } + + @Override + public , C> Map toMapList(PreparedStatement ps, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMapList(ps, mapKeyType, componentType, bindObjects); + } + + @Override + public , C> Map toMapList(String sql, Class mapKeyType, Class componentType, final Object... bindObjects) throws SQLException { + return delegate.toMapList(prepareSql(sql, bindObjects), mapKeyType, componentType, bindObjects); + } + + @Override + public , E extends Map, V> T toCollectionMap(PreparedStatement ps, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toCollectionMap(ps, collectionType, componentType, mapValType, bindObjects); + } + + @Override + public , E extends Map, V> T toCollectionMap(String sql, final Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toCollectionMap(prepareSql(sql, bindObjects), collectionType, componentType, mapValType, bindObjects); + } + + @Override + public , E extends Map, V> T toCollectionMap(PreparedStatement ps, T list, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toCollectionMap(ps, list, componentType, mapValType, bindObjects); + } + + @Override + public , E extends Map, V> T toCollectionMap(String sql, T list, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toCollectionMap(prepareSql(sql, bindObjects), list, componentType, mapValType, bindObjects); + } + + @Override + public , K, E extends Map, V> T toMapMap(PreparedStatement ps, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(ps, returnType, mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , K, E extends Map, V> T toMapMap(String sql, final Class returnType, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(prepareSql(sql, bindObjects), returnType, mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , K, E extends Map, V> T toMapMap(PreparedStatement ps, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(ps, map, mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , K, E extends Map, V> T toMapMap(String sql, T map, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(prepareSql(sql, bindObjects), map, mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(PreparedStatement ps, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollectionMap(ps, returnType, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String sql, final Class returnType, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollectionMap(prepareSql(sql, bindObjects), returnType, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(PreparedStatement ps, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollectionMap(ps, map, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } + + @Override + public , K, C extends Collection, E extends Map, V> T toMapCollectionMap(String sql, T map, Class mapKeyType, Class collectionType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapCollectionMap(prepareSql(sql, bindObjects), map, mapKeyType, collectionType, componentType, mapValType, bindObjects); + } + + @Override + public , V> ListIterator> toListIteratorMap(PreparedStatement ps, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListIteratorMap(ps, type, mapValType, bindObjects); + } + + @Override + public , V> ListIterator> toListIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListIteratorMap(prepareSql(sql, bindObjects), type, mapValType, bindObjects); + } + + @Override + public , V> Iterator> toIteratorMap(PreparedStatement ps, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toIteratorMap(ps, type, mapValType, bindObjects); + } + + @Override + public , V> Iterator> toIteratorMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toIteratorMap(prepareSql(sql, bindObjects), type, mapValType, bindObjects); + } + + @Override + public , V> Map[] toArrayMap(PreparedStatement ps, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toArrayMap(ps, type, mapValType, bindObjects); + } + + @Override + public , V> Map[] toArrayMap(String sql, final Class type, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toArrayMap(prepareSql(sql, bindObjects), type, mapValType, bindObjects); + } + + @Override + public , V> List> toListMap(PreparedStatement ps, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListMap(ps, componentType, mapValType, bindObjects); + } + + @Override + public , V> List> toListMap(String sql, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListMap(prepareSql(sql, bindObjects), componentType, mapValType, bindObjects); + } + + @Override + public , V> Map> toMapMap(PreparedStatement ps, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(ps, mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , V> Map> toMapMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(prepareSql(sql, bindObjects), mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , V> Map>> toMapListMap(PreparedStatement ps, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapListMap(ps, mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public , V> Map>> toMapListMap(String sql, Class mapKeyType, Class componentType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapListMap(prepareSql(sql, bindObjects), mapKeyType, componentType, mapValType, bindObjects); + } + + @Override + public ListIterator> toListIteratorMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListIteratorMap(ps, mapValType, bindObjects); + } + + @Override + public ListIterator> toListIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListIteratorMap(prepareSql(sql, bindObjects), mapValType, bindObjects); + } + + @Override + public Iterator> toIteratorMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toIteratorMap(ps, mapValType, bindObjects); + } + + @Override + public Iterator> toIteratorMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toIteratorMap(prepareSql(sql, bindObjects), mapValType, bindObjects); + } + + @Override + public List> toListMap(PreparedStatement ps, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListMap(ps, mapValType, bindObjects); + } + + @Override + public List> toListMap(String sql, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toListMap(prepareSql(sql, bindObjects), mapValType, bindObjects); + } + + @Override + public Map> toMapMap(PreparedStatement ps, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(ps, mapKeyType, mapValType, bindObjects); + } + + @Override + public Map> toMapMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapMap(prepareSql(sql, bindObjects), mapKeyType, mapValType, bindObjects); + } + + @Override + public Map>> toMapListMap(PreparedStatement ps, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapListMap(ps, mapKeyType, mapValType, bindObjects); + } + + @Override + public Map>> toMapListMap(String sql, Class mapKeyType, Class mapValType, final Object... bindObjects) throws SQLException { + return delegate.toMapListMap(prepareSql(sql, bindObjects), mapKeyType, mapValType, bindObjects); + } + +} + diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java index 5484e01..786fd5f 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/NullQueryMapper.java @@ -56,6 +56,10 @@ public class NullQueryMapper extends QueryMapper { this(jndiName, cm, true); } + public static NullQueryMapper wrap(final QueryMapper qm){ + return qm instanceof NullQueryMapper ? (NullQueryMapper)qm : new NullQueryMapper(qm); + } + // these update the database @Override @@ -201,16 +205,6 @@ public class NullQueryMapper extends QueryMapper { delegate.close(); } - @Override - protected PreparedStatement bind(PreparedStatement ps, Object... bindObjects) throws SQLException { - return delegate.bind(ps, bindObjects); - } - - @Override - protected ResultSet bindExecute(PreparedStatement ps, Object... bindObjects) throws SQLException { - return delegate.bindExecute(ps, bindObjects); - } - // and these are standard @Override diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/OracleArrayInList.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/OracleArrayInList.java new file mode 100644 index 0000000..f78263a --- /dev/null +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/OracleArrayInList.java @@ -0,0 +1,62 @@ +package com.moparisthebest.jdbc; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Array; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collection; + +/** + * Created by mopar on 4/29/15. + */ +public class OracleArrayInList extends ArrayInList { + + private static final InList instance = new OracleArrayInList(); + + public static InList instance() { + return instance; + } + + private static final Class oracleConnection; + private static final Method createArray; + + static { + Class oc; + Method ca; + try { + oc = Class.forName("oracle.jdbc.OracleConnection"); + ca = oc.getDeclaredMethod("createOracleArray", String.class, Object.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + oracleConnection = oc; + createArray = ca; + } + + protected OracleArrayInList() { + } + + protected String columnAppend() { + return " IN(select column_value from table(?))"; + } + + protected Array toArray(final Connection conn, final Collection values) throws SQLException { + /* + return conn.unwrap(oracle.jdbc.OracleConnection.class).createOracleArray( + values.iterator().next() instanceof Number ? "ARRAY_NUM_TYPE" : "ARRAY_STR_TYPE", + values.toArray() + ); + */ + try { + return (Array) createArray.invoke(conn.unwrap(oracleConnection), + values.iterator().next() instanceof Number ? "ARRAY_NUM_TYPE" : "ARRAY_STR_TYPE", + values.toArray() + ); + } catch (SQLException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); // should never happen, but if it does don't hide it + } + } +} diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java index f4e492b..fb483bf 100644 --- a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java +++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/jdbc/QueryMapper.java @@ -155,6 +155,8 @@ public class QueryMapper implements Closeable { } else if (o instanceof Blob) ps.setBlob(index, (Blob) o); + else if (o instanceof ArrayInList.ArrayListObject) + ps.setArray(index, ((ArrayInList.ArrayListObject) o).getArray()); else ps.setObject(index, o); // probably won't get here ever, but just in case... /* @@ -201,7 +203,13 @@ public class QueryMapper implements Closeable { if (bindObjects != null && bindObjects.length > 0) { for (Object o : bindObjects) { if (o != null) { - if (o instanceof Object[]) { + if (o == InList.InListObject.empty) { + continue; // ignore + } else if (o instanceof BindInList.BindInListObject) { + if (((BindInList.BindInListObject) o).getBindObjects() != null) + index = recursiveBind(ps, index, ((BindInList.BindInListObject) o).getBindObjects()); + continue; + } else if (o instanceof Object[]) { index = recursiveBind(ps, index, (Object[]) o); continue; } else if (o instanceof Collection) {