mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-12-21 23:08:52 -05:00
Implement OptimalInList.java which picks the optimal in list based on connection type
This commit is contained in:
parent
9c1efdc715
commit
af0c0f3483
@ -33,9 +33,15 @@ script:
|
||||
- docker ps -a
|
||||
# java8+ supports everything
|
||||
- mvn -B -pl '!test' clean install || travis_terminate 1;
|
||||
# everything against BIND
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=BIND --settings .travis-settings.xml -P oracle '-DjdbcUrl1=jdbc:postgresql:test_db' '-DjdbcUrl2=jdbc:mariadb://127.0.0.1:3306/test_db?user=root' '-DjdbcUrl3=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' '-DjdbcUrl4=jdbc:sqlserver://localhost:1433;databaseName=master;username=sa;password=<YourStrong!Passw0rd>;' || travis_terminate 1;
|
||||
# everything against BIND jdbcMapper, but OPTIMAL queryMapper
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=BIND -DqueryMapper.databaseType=OPTIMAL --settings .travis-settings.xml -P oracle '-DjdbcUrl1=jdbc:postgresql:test_db' '-DjdbcUrl2=jdbc:mariadb://127.0.0.1:3306/test_db?user=root' '-DjdbcUrl3=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' '-DjdbcUrl4=jdbc:sqlserver://localhost:1433;databaseName=master;username=sa;password=<YourStrong!Passw0rd>;' || travis_terminate 1;
|
||||
# h2 and postgre against ANY
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=ANY '-DjdbcUrl=h2' '-DjdbcUrl1=jdbc:postgresql:test_db' || travis_terminate 1;
|
||||
# oracle against ORACLE
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=ORACLE --settings .travis-settings.xml -P oracle '-DjdbcUrl=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' || travis_terminate 1;
|
||||
# hsql against UNNEST
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=UNNEST '-DjdbcUrl=hsqldb' || travis_terminate 1;
|
||||
|
||||
matrix:
|
||||
@ -49,6 +55,7 @@ matrix:
|
||||
# java6 doesn't support ms-sql at all, and doesn't support h2 with ANY
|
||||
- mvn -B -pl '!test' clean install || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=BIND --settings .travis-settings.xml -P oracle '-DjdbcUrl1=jdbc:postgresql:test_db' '-DjdbcUrl2=jdbc:mariadb://127.0.0.1:3306/test_db?user=root' '-DjdbcUrl3=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=BIND -DqueryMapper.databaseType=OPTIMAL --settings .travis-settings.xml -P oracle '-DjdbcUrl1=jdbc:postgresql:test_db' '-DjdbcUrl2=jdbc:mariadb://127.0.0.1:3306/test_db?user=root' '-DjdbcUrl3=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=ANY '-DjdbcUrl=jdbc:postgresql:test_db' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=ORACLE --settings .travis-settings.xml -P oracle '-DjdbcUrl=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=UNNEST '-DjdbcUrl=hsqldb' || travis_terminate 1;
|
||||
@ -59,6 +66,7 @@ matrix:
|
||||
# java7 doesn't support h2 with ANY
|
||||
- mvn -B -pl '!test' clean install || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=BIND --settings .travis-settings.xml -P oracle '-DjdbcUrl1=jdbc:postgresql:test_db' '-DjdbcUrl2=jdbc:mariadb://127.0.0.1:3306/test_db?user=root' '-DjdbcUrl3=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' '-DjdbcUrl4=jdbc:sqlserver://localhost:1433;databaseName=master;username=sa;password=<YourStrong!Passw0rd>;' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=BIND -DqueryMapper.databaseType=OPTIMAL --settings .travis-settings.xml -P oracle '-DjdbcUrl1=jdbc:postgresql:test_db' '-DjdbcUrl2=jdbc:mariadb://127.0.0.1:3306/test_db?user=root' '-DjdbcUrl3=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' '-DjdbcUrl4=jdbc:sqlserver://localhost:1433;databaseName=master;username=sa;password=<YourStrong!Passw0rd>;' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=ANY '-DjdbcUrl=jdbc:postgresql:test_db' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=ORACLE --settings .travis-settings.xml -P oracle '-DjdbcUrl=jdbc:oracle:thin:travis_test/travis_test@127.0.0.1:1521/xe' || travis_terminate 1;
|
||||
- mvn -B -pl test clean test -DjdbcMapper.databaseType=UNNEST '-DjdbcUrl=hsqldb' || travis_terminate 1;
|
||||
|
@ -29,6 +29,11 @@ public class ArrayInList implements InList {
|
||||
this(JdbcMapper.DatabaseType.ANY.arrayNumberTypeName, JdbcMapper.DatabaseType.ANY.arrayStringTypeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InList instance(Connection conn) {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected String columnAppendIn(final String columnName) {
|
||||
return "(" + columnName + " = ANY(?))";
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ public class BindInList implements InList {
|
||||
this(defaultMaxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InList instance(Connection conn) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T> InListObject inList(final Connection conn, final String columnName, final Collection<T> values) {
|
||||
return values == null || values.isEmpty() ? InListObject.inEmpty : new BindInListObject(
|
||||
toInList(columnName, values, this.maxSize),
|
||||
|
@ -12,6 +12,13 @@ import java.util.Collection;
|
||||
*/
|
||||
public interface InList {
|
||||
|
||||
/**
|
||||
* Returns an InList instance for use with this connection
|
||||
* @param conn connection which may be inspected to determine best InList to use
|
||||
* @return InList instance
|
||||
*/
|
||||
public InList instance(final Connection conn);
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.moparisthebest.jdbc;
|
||||
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@ -17,11 +18,38 @@ public class ListQueryMapper extends QueryMapper {
|
||||
private static final InList defaultInList;
|
||||
|
||||
static {
|
||||
InList def = null;
|
||||
InList def;
|
||||
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);
|
||||
final String inListClassName = System.getProperty("QueryMapper.defaultInList.class");
|
||||
if(inListClassName != null) {
|
||||
final Class<?> inListClass = Class.forName(inListClassName);
|
||||
final Method method = inListClass.getMethod(System.getProperty("QueryMapper.defaultInList.method", "instance"));
|
||||
def = (InList) method.invoke(null);
|
||||
} else {
|
||||
// todo: change default to OPTIMAL ?
|
||||
final String type = System.getProperty("queryMapper.databaseType", System.getProperty("jdbcMapper.databaseType", "BIND"));
|
||||
if(type.equals("OPTIMAL")) {
|
||||
def = OptimalInList.instance();
|
||||
} else {
|
||||
switch (JdbcMapper.DatabaseType.valueOf(type)) {
|
||||
case DEFAULT:
|
||||
case BIND:
|
||||
def = BindInList.instance();
|
||||
break;
|
||||
case ANY:
|
||||
def = ArrayInList.instance();
|
||||
break;
|
||||
case ORACLE:
|
||||
def = OracleArrayInList.instance();
|
||||
break;
|
||||
case UNNEST:
|
||||
def = UnNestArrayInList.instance();
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Invalid queryMapper.databaseType: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// NEVER ignore
|
||||
throw new RuntimeException(e);
|
||||
@ -35,7 +63,7 @@ public class ListQueryMapper extends QueryMapper {
|
||||
public static final String inListReplace = "{inList}";
|
||||
|
||||
private ListQueryMapper(Connection conn, String jndiName, Factory<Connection> factory, QueryMapper delegate, ResultSetMapper cm, InList inList) {
|
||||
this.inList = inList;
|
||||
this.inList = inList.instance(conn);
|
||||
this.delegate = delegate == null ? new QueryMapper(conn, jndiName, factory, cm) :
|
||||
(delegate instanceof ListQueryMapper ? ((ListQueryMapper)delegate).delegate : delegate);
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
package com.moparisthebest.jdbc;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
public class OptimalInList implements InList {
|
||||
|
||||
private static final InList instance;
|
||||
|
||||
public static InList instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static final Class<?> hsqlConnection, oracleConnection, postgreConnection, h2Connection;
|
||||
|
||||
static {
|
||||
hsqlConnection = classForName("org.hsqldb.jdbc.JDBCConnection");
|
||||
oracleConnection = classForName("oracle.jdbc.OracleConnection");
|
||||
postgreConnection = classForName("org.postgresql.PGConnection");
|
||||
h2Connection = classForName("org.h2.jdbc.JdbcConnection");
|
||||
instance = new OptimalInList();
|
||||
}
|
||||
|
||||
public static Class<?> classForName(final String className) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWrapperFor(final Connection conn, final Class<?> connectionClass) {
|
||||
if (connectionClass == null)
|
||||
return false;
|
||||
try {
|
||||
return conn.isWrapperFor(connectionClass);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final InList any, oracle, unnest, bind;
|
||||
|
||||
public OptimalInList(final InList any, final InList oracle, final InList unnest, final InList bind) {
|
||||
this.any = any;
|
||||
this.oracle = oracle;
|
||||
this.unnest = unnest;
|
||||
this.bind = bind;
|
||||
}
|
||||
|
||||
protected OptimalInList() {
|
||||
this(ArrayInList.instance(), OracleArrayInList.instance(), UnNestArrayInList.instance(), BindInList.instance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InList instance(Connection conn) {
|
||||
if (isWrapperFor(conn, postgreConnection)
|
||||
// java6 version of h2 doesn't support this
|
||||
//IFJAVA8_START
|
||||
|| isWrapperFor(conn, h2Connection)
|
||||
//IFJAVA8_END
|
||||
)
|
||||
return any;
|
||||
if (isWrapperFor(conn, oracleConnection))
|
||||
return oracle;
|
||||
if (isWrapperFor(conn, hsqlConnection))
|
||||
return unnest;
|
||||
// works for everything
|
||||
return bind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't call this which inspect connection type each time, get an InList once with .instance(Connection) and use it forever
|
||||
*
|
||||
* @param conn
|
||||
* @param columnName Column name for query
|
||||
* @param values values for in list
|
||||
* @param <T>
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Override
|
||||
public <T> InListObject inList(Connection conn, String columnName, Collection<T> values) throws SQLException {
|
||||
return this.instance(conn).inList(conn, columnName, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't call this which inspect connection type each time, get an InList once with .instance(Connection) and use it forever
|
||||
*
|
||||
* @param conn
|
||||
* @param columnName Column name for query
|
||||
* @param values values for in list
|
||||
* @param <T>
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Override
|
||||
public <T> InListObject notInList(Connection conn, String columnName, Collection<T> values) throws SQLException {
|
||||
return this.instance(conn).notInList(conn, columnName, values);
|
||||
}
|
||||
}
|
@ -2,12 +2,12 @@ package com.moparisthebest.jdbc;
|
||||
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
||||
|
||||
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;
|
||||
|
||||
import static com.moparisthebest.jdbc.OptimalInList.oracleConnection;
|
||||
|
||||
/**
|
||||
* Created by mopar on 4/29/15.
|
||||
@ -20,19 +20,16 @@ public class OracleArrayInList extends ArrayInList {
|
||||
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;
|
||||
Method ca = null;
|
||||
if(oracleConnection != null)
|
||||
try {
|
||||
ca = oracleConnection.getDeclaredMethod("createOracleArray", String.class, Object.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
createArray = ca;
|
||||
}
|
||||
|
||||
|
@ -55,82 +55,12 @@ public class QueryMapperQmDao implements QmDao {
|
||||
public static final String selectNumVal = "SELECT num_val FROM val WHERE val_no = ?";
|
||||
public static final String selectStrVal = "SELECT str_val FROM val WHERE val_no = ?";
|
||||
|
||||
private static final Collection<Class<?>> noArrayInListSupport;
|
||||
public static final Class<?> hsqlConnection, oracleConnection, mssqlConnection;
|
||||
|
||||
static {
|
||||
Collection<Class<?>> no = new ArrayList<Class<?>>();
|
||||
for(final String connectionClassName : new String[]{
|
||||
"org.apache.derby.iapi.jdbc.EngineConnection"
|
||||
, "org.hsqldb.jdbc.JDBCConnection" // does not support ArrayInList but *does* support UnNestArrayInList
|
||||
, "org.sqlite.jdbc3.JDBC3Connection"
|
||||
, "org.mariadb.jdbc.MariaDbConnection"
|
||||
, "com.microsoft.sqlserver.jdbc.ISQLServerConnection"
|
||||
, "oracle.jdbc.OracleConnection" // does not support ArrayInList but *does* support OracleArrayInList
|
||||
// h2 doesn't support this with java6 either...
|
||||
/*IFJAVA6_START
|
||||
, "org.h2.jdbc.JdbcConnection"
|
||||
IFJAVA6_END*/
|
||||
})
|
||||
try {
|
||||
no.add(Class.forName(connectionClassName));
|
||||
} catch(Exception e) {
|
||||
// ignore
|
||||
}
|
||||
noArrayInListSupport = Collections.unmodifiableCollection(no);
|
||||
hsqlConnection = classForName("org.hsqldb.jdbc.JDBCConnection");
|
||||
oracleConnection = classForName("oracle.jdbc.OracleConnection");
|
||||
mssqlConnection = classForName("com.microsoft.sqlserver.jdbc.SQLServerConnection");
|
||||
}
|
||||
|
||||
private static Class<?> classForName(final String className) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch(Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWrapperFor(final Connection conn, final Class<?> connectionClass) {
|
||||
if(connectionClass == null)
|
||||
return false;
|
||||
try {
|
||||
return conn.isWrapperFor(connectionClass);
|
||||
} catch(Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean supportsArrayInList(final Connection conn) {
|
||||
for(final Class<?> connectionClass : noArrayInListSupport) {
|
||||
try {
|
||||
if(conn.isWrapperFor(connectionClass))
|
||||
return false;
|
||||
} catch (SQLException e) {
|
||||
// ignore... how could this happen?
|
||||
}
|
||||
}
|
||||
// assume Connections DO support this unless we KNOW otherwise
|
||||
return true;
|
||||
}
|
||||
|
||||
public static InList getBestInList(final Connection conn) {
|
||||
if(isWrapperFor(conn, oracleConnection))
|
||||
return OracleArrayInList.instance();
|
||||
if(isWrapperFor(conn, hsqlConnection))
|
||||
return UnNestArrayInList.instance();
|
||||
if(supportsArrayInList(conn))
|
||||
return ArrayInList.instance();
|
||||
// works for everything
|
||||
return BindInList.instance();
|
||||
}
|
||||
|
||||
protected final QueryMapper qm;
|
||||
protected final ListQueryMapper lqm;
|
||||
|
||||
public QueryMapperQmDao(final Connection conn, final ResultSetMapper rsm) {
|
||||
this.qm = new QueryMapper(conn, rsm);
|
||||
this.lqm = new ListQueryMapper(qm, getBestInList(qm.getConnection()));
|
||||
this.lqm = new ListQueryMapper(qm);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,8 +22,10 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
//IFJAVA8_END
|
||||
|
||||
import static com.moparisthebest.jdbc.OptimalInList.classForName;
|
||||
import static com.moparisthebest.jdbc.OptimalInList.isWrapperFor;
|
||||
import static com.moparisthebest.jdbc.OptimalInList.oracleConnection;
|
||||
import static com.moparisthebest.jdbc.TryClose.tryClose;
|
||||
import static com.moparisthebest.jdbc.codegen.QueryMapperQmDao.*;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@ -72,6 +74,8 @@ public class QueryMapperTest {
|
||||
public static final Collection<String> jdbcUrls;
|
||||
public static final Map<String, DataSource> dataSources = new HashMap<String, DataSource>();
|
||||
|
||||
public static final Class<?> mssqlConnection = classForName("com.microsoft.sqlserver.jdbc.ISQLServerConnection");
|
||||
|
||||
static {
|
||||
final Collection<String> jUrls = new ArrayList<String>();
|
||||
final String jdbcUrl = System.getProperty("jdbcUrl", "all");
|
||||
|
Loading…
Reference in New Issue
Block a user