Fix QueryMapper toResultSet and insertGetGeneratedKeyType to use PreparedStatementFactory and become un-ambiguous

This commit is contained in:
Travis Burtrum 2018-10-09 22:53:49 -04:00
parent 278e99e894
commit a490154334
5 changed files with 74 additions and 107 deletions

View File

@ -93,47 +93,19 @@ public class CachingQueryMapper extends QueryMapper {
}
protected PreparedStatement getPreparedStatement(String sql) throws SQLException {
return getPreparedStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
return getPreparedStatement(sql, defaultPsf);
}
protected PreparedStatement getPreparedStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
/**
* This could perhaps end up caching something it shouldn't, as psf could return a different PreparedStatement
* for a given sql, we are going to assume this would never happen, if it could, don't use CachingQueryMapper or
* override this method, whatever you want.
*/
protected PreparedStatement getPreparedStatement(final String sql, final PreparedStatementFactory psf) throws SQLException {
PreparedStatement ps = cache.get(sql);
if (ps == null) {
//System.out.println("cache miss");
ps = conn.prepareStatement(sql,resultSetType,resultSetConcurrency);
cache.put(sql, ps);
}
//else System.out.println("cache hit");
return ps;
}
protected PreparedStatement getInsertPreparedStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
PreparedStatement ps = cache.get(sql);
if (ps == null) {
//System.out.println("cache miss");
ps = conn.prepareStatement(sql, autoGeneratedKeys);
cache.put(sql, ps);
}
//else System.out.println("cache hit");
return ps;
}
protected PreparedStatement getInsertPreparedStatement(final String sql, final int[] columnIndexes) throws SQLException {
PreparedStatement ps = cache.get(sql);
if (ps == null) {
//System.out.println("cache miss");
ps = conn.prepareStatement(sql, columnIndexes);
cache.put(sql, ps);
}
//else System.out.println("cache hit");
return ps;
}
protected PreparedStatement getInsertPreparedStatement(final String sql, final String[] columnNames) throws SQLException {
PreparedStatement ps = cache.get(sql);
if (ps == null) {
//System.out.println("cache miss");
ps = conn.prepareStatement(sql, columnNames);
ps = psf.prepareStatement(conn, sql);
cache.put(sql, ps);
}
//else System.out.println("cache hit");
@ -171,34 +143,19 @@ public class CachingQueryMapper extends QueryMapper {
@Override
public Long insertGetGeneratedKey(String sql, Object... bindObjects) throws SQLException {
return super.insertGetGeneratedKey(getInsertPreparedStatement(sql, Statement.RETURN_GENERATED_KEYS), bindObjects);
return super.insertGetGeneratedKey(getPreparedStatement(sql, getSingleColumnPreparedStatementFactory()), bindObjects);
}
@Override
public <T> T insertGetGeneratedKeyType(String sql, TypeReference<T> typeReference, Object... bindObjects) throws SQLException {
return super.insertGetGeneratedKeyType(getInsertPreparedStatement(sql, Statement.RETURN_GENERATED_KEYS), typeReference, bindObjects);
}
@Override
public <T> T insertGetGeneratedKeyType(String sql, int[] columnIndexes, TypeReference<T> typeReference, Object... bindObjects) throws SQLException {
return super.insertGetGeneratedKeyType(getInsertPreparedStatement(sql, columnIndexes), typeReference, bindObjects);
}
@Override
public <T> T insertGetGeneratedKeyType(String sql, String[] columnNames, TypeReference<T> typeReference, Object... bindObjects) throws SQLException {
return super.insertGetGeneratedKeyType(getInsertPreparedStatement(sql, columnNames), typeReference, bindObjects);
public <T> T insertGetGeneratedKeyType(String sql, final PreparedStatementFactory psf, TypeReference<T> typeReference, Object... bindObjects) throws SQLException {
return super.insertGetGeneratedKeyType(getPreparedStatement(sql, psf), typeReference, bindObjects);
}
// these grab ResultSets from the database
@Override
public ResultSet toResultSet(String sql, Object... bindObjects) throws SQLException {
return super.toResultSet(getPreparedStatement(sql), bindObjects);
}
@Override
public ResultSet toResultSet(String sql, int rsType, int rsConcurrency, Object... bindObjects) throws SQLException {
return super.toResultSet(getPreparedStatement(sql,rsType,rsConcurrency), bindObjects);
public ResultSet toResultSet(final String sql, final PreparedStatementFactory psf, final Object... bindObjects) throws SQLException {
return super.toResultSet(getPreparedStatement(sql, psf), bindObjects);
}
// DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh

View File

@ -293,8 +293,8 @@ public class ListQueryMapper extends QueryMapper {
}
@Override
public ResultSet toResultSet(String sql, int rsType, int rsConcurrency, Object... bindObjects) throws SQLException {
return delegate.toResultSet(prepareSql(sql, bindObjects), rsType, rsConcurrency, bindObjects);
public ResultSet toResultSet(String sql, PreparedStatementFactory psf, Object... bindObjects) throws SQLException {
return delegate.toResultSet(prepareSql(sql, bindObjects), psf, bindObjects);
}
@Override
@ -308,13 +308,8 @@ public class ListQueryMapper extends QueryMapper {
}
@Override
public <T> T insertGetGeneratedKeyType(String sql, int[] columnIndexes, TypeReference<T> typeReference, Object... bindObjects) throws SQLException {
return delegate.insertGetGeneratedKeyType(prepareSql(sql, bindObjects), columnIndexes, typeReference, bindObjects);
}
@Override
public <T> T insertGetGeneratedKeyType(String sql, String[] columnNames, TypeReference<T> typeReference, Object... bindObjects) throws SQLException {
return delegate.insertGetGeneratedKeyType(prepareSql(sql, bindObjects), columnNames, typeReference, bindObjects);
public <T> T insertGetGeneratedKeyType(String sql, PreparedStatementFactory psf, TypeReference<T> typeReference, Object... bindObjects) throws SQLException {
return delegate.insertGetGeneratedKeyType(prepareSql(sql, bindObjects), psf, typeReference, bindObjects);
}
// DO NOT EDIT BELOW THIS LINE, OR CHANGE THIS COMMENT, CODE AUTOMATICALLY GENERATED BY genQueryMapper.sh

View File

@ -165,19 +165,9 @@ public class NullQueryMapper extends QueryMapper {
}
@Override
public <T> T insertGetGeneratedKeyType(String sql, int[] columnIndexes, TypeReference<T> typeReference, Object... bindObjects) {
public <T> T insertGetGeneratedKeyType(String sql, PreparedStatementFactory psf, TypeReference<T> typeReference, Object... bindObjects) {
try {
return delegate.insertGetGeneratedKeyType(sql, columnIndexes, typeReference, bindObjects);
} catch (Throwable e) {
if (verbose) e.printStackTrace();
}
return null;
}
@Override
public <T> T insertGetGeneratedKeyType(String sql, String[] columnNames, TypeReference<T> typeReference, Object... bindObjects) {
try {
return delegate.insertGetGeneratedKeyType(sql, columnNames, typeReference, bindObjects);
return delegate.insertGetGeneratedKeyType(sql, psf, typeReference, bindObjects);
} catch (Throwable e) {
if (verbose) e.printStackTrace();
}
@ -269,9 +259,9 @@ public class NullQueryMapper extends QueryMapper {
}
@Override
public ResultSet toResultSet(String sql, int rsType, int rsConcurrency, Object... bindObjects) throws SQLException {
public ResultSet toResultSet(String sql, PreparedStatementFactory psf, Object... bindObjects) {
try {
return delegate.toResultSet(sql, rsType, rsConcurrency, bindObjects);
return delegate.toResultSet(sql, psf, bindObjects);
} catch (Throwable e) {
if (verbose) e.printStackTrace();
}

View File

@ -0,0 +1,9 @@
package com.moparisthebest.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public interface PreparedStatementFactory {
PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException;
}

View File

@ -25,7 +25,32 @@ public class QueryMapper implements JdbcMapper {
protected static final int[] ORACLE_SINGLE_COLUMN_INDEX = new int[]{1};
//IFJAVA8_START
public static final PreparedStatementFactory oracleSingleColumnPsf = (conn, sql) -> conn.prepareStatement(sql, ORACLE_SINGLE_COLUMN_INDEX);
public static final PreparedStatementFactory standardReturnGeneratedKeysPsf = (conn, sql) -> conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
public static final PreparedStatementFactory defaultPsf = Connection::prepareStatement;
//IFJAVA8_END
/*IFJAVA6_START
public static final PreparedStatementFactory oracleSingleColumnPsf = new PreparedStatementFactory() {
@Override
public PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
return conn.prepareStatement(sql, ORACLE_SINGLE_COLUMN_INDEX);
}
};
public static final PreparedStatementFactory standardReturnGeneratedKeysPsf = new PreparedStatementFactory() {
@Override
public PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
}
};
public static final PreparedStatementFactory defaultPsf = new PreparedStatementFactory() {
@Override
public PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
return conn.prepareStatement(sql);
}
};
private static final Charset UTF_8 = Charset.forName("UTF-8");
IFJAVA6_END*/
@ -332,20 +357,27 @@ public class QueryMapper implements JdbcMapper {
}
}
private Boolean oracleDatabase = null;
public Long insertGetGeneratedKey(final String sql, final Object... bindObjects) throws SQLException {
private PreparedStatementFactory singleColumnPsf = null;
protected PreparedStatementFactory getSingleColumnPreparedStatementFactory() {
// this single function is somewhat database specific
// sqlite/ms-sql/mysql works with either Statement.RETURN_GENERATED_KEYS or int[]{1}
// oracle requires int[]{1} instead, failing on Statement.RETURN_GENERATED_KEYS
// postgre requires Statement.RETURN_GENERATED_KEYS instead, failing on int[]{1}
// so we lazily cache oracleDatabase just in this one function
if(oracleDatabase == null)
oracleDatabase = OptimalInList.isWrapperFor(conn, OptimalInList.oracleConnection);
if(singleColumnPsf == null) {
if(OptimalInList.isWrapperFor(conn, OptimalInList.oracleConnection))
singleColumnPsf = oracleSingleColumnPsf;
else
singleColumnPsf = standardReturnGeneratedKeysPsf;
}
return singleColumnPsf;
}
public Long insertGetGeneratedKey(final String sql, final Object... bindObjects) throws SQLException {
PreparedStatement ps = null;
try {
ps = oracleDatabase ? conn.prepareStatement(sql, ORACLE_SINGLE_COLUMN_INDEX) : conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps = getSingleColumnPreparedStatementFactory().prepareStatement(conn, sql);
return this.insertGetGeneratedKey(ps, bindObjects);
} finally {
tryClose(ps);
@ -353,29 +385,13 @@ public class QueryMapper implements JdbcMapper {
}
public <T> T insertGetGeneratedKeyType(final String sql, final TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
return this.insertGetGeneratedKeyType(ps, typeReference, bindObjects);
} finally {
tryClose(ps);
}
return insertGetGeneratedKeyType(sql, standardReturnGeneratedKeysPsf, typeReference, bindObjects);
}
public <T> T insertGetGeneratedKeyType(final String sql, final int[] columnIndexes, final TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
public <T> T insertGetGeneratedKeyType(final String sql, final PreparedStatementFactory psf, final TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql, columnIndexes);
return this.insertGetGeneratedKeyType(ps, typeReference, bindObjects);
} finally {
tryClose(ps);
}
}
public <T> T insertGetGeneratedKeyType(final String sql, final String[] columnNames, final TypeReference<T> typeReference, final Object... bindObjects) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql, columnNames);
ps = psf.prepareStatement(conn, sql);
return this.insertGetGeneratedKeyType(ps, typeReference, bindObjects);
} finally {
tryClose(ps);
@ -422,17 +438,17 @@ public class QueryMapper implements JdbcMapper {
return bindExecute(ps, bindObjects);
}
public ResultSet toResultSet(String sql, final Object... bindObjects) throws SQLException {
return toResultSet(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, bindObjects);
public ResultSet toResultSet(final String sql, final Object... bindObjects) throws SQLException {
return toResultSet(sql, defaultPsf, bindObjects);
}
public ResultSet toResultSet(String sql, int rsType, int rsConcurrency, final Object... bindObjects) throws SQLException {
public ResultSet toResultSet(final String sql, final PreparedStatementFactory psf, final Object... bindObjects) throws SQLException {
// works with StatementClosingResultSet
boolean error = true;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql, rsType, rsConcurrency);
ps = psf.prepareStatement(conn, sql);
rs = this.toResultSet(ps, bindObjects);
error = false;
return new StatementClosingResultSet(rs, ps);