mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-11-28 11:52:17 -05:00
Implement optional compile-time SQL checking with explain plan
This commit is contained in:
parent
1fa08bc74b
commit
a6c5fc7b52
@ -1,7 +1,9 @@
|
|||||||
package com.moparisthebest.jdbc.codegen;
|
package com.moparisthebest.jdbc.codegen;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.ArrayInList;
|
||||||
import com.moparisthebest.jdbc.Cleaner;
|
import com.moparisthebest.jdbc.Cleaner;
|
||||||
import com.moparisthebest.jdbc.MapperException;
|
import com.moparisthebest.jdbc.MapperException;
|
||||||
|
import com.moparisthebest.jdbc.OracleArrayInList;
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
import javax.annotation.processing.*;
|
||||||
import javax.lang.model.SourceVersion;
|
import javax.lang.model.SourceVersion;
|
||||||
@ -28,7 +30,7 @@ import static com.moparisthebest.jdbc.codegen.JdbcMapperFactory.SUFFIX;
|
|||||||
* Created by mopar on 5/24/17.
|
* Created by mopar on 5/24/17.
|
||||||
*/
|
*/
|
||||||
@SupportedAnnotationTypes("com.moparisthebest.jdbc.codegen.JdbcMapper.Mapper")
|
@SupportedAnnotationTypes("com.moparisthebest.jdbc.codegen.JdbcMapper.Mapper")
|
||||||
@SupportedOptions({"jdbcMapper.databaseType", "jdbcMapper.arrayNumberTypeName", "jdbcMapper.arrayStringTypeName", "JdbcMapper.allowedMaxRowParamNames"})
|
@SupportedOptions({"jdbcMapper.databaseType", "jdbcMapper.arrayNumberTypeName", "jdbcMapper.arrayStringTypeName", "jdbcMapper.allowedMaxRowParamNames", "jdbcMapper.sqlCheckerClass"})
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_5)
|
@SupportedSourceVersion(SourceVersion.RELEASE_5)
|
||||||
public class JdbcMapperProcessor extends AbstractProcessor {
|
public class JdbcMapperProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
@ -47,8 +49,8 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
RELEASE_8 = rl8;
|
RELEASE_8 = rl8;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Types types;
|
static Types types;
|
||||||
private static Messager messager;
|
static Messager messager;
|
||||||
|
|
||||||
public static Types getTypes() {
|
public static Types getTypes() {
|
||||||
return types;
|
return types;
|
||||||
@ -58,14 +60,15 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
return messager;
|
return messager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypeMirror sqlExceptionType, stringType, numberType, utilDateType, readerType, clobType, jdbcMapperType,
|
static TypeMirror sqlExceptionType, stringType, numberType, utilDateType, readerType, clobType, jdbcMapperType,
|
||||||
byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, calendarType, cleanerType;
|
byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, calendarType, cleanerType, enumType;
|
||||||
//IFJAVA8_START
|
//IFJAVA8_START
|
||||||
private TypeMirror instantType, localDateTimeType, localDateType, localTimeType, zonedDateTimeType, offsetDateTimeType, offsetTimeType;
|
static TypeMirror instantType, localDateTimeType, localDateType, localTimeType, zonedDateTimeType, offsetDateTimeType, offsetTimeType;
|
||||||
//IFJAVA8_END
|
//IFJAVA8_END
|
||||||
private TypeElement cleanerElement;
|
private TypeElement cleanerElement;
|
||||||
private JdbcMapper.DatabaseType defaultDatabaseType;
|
private JdbcMapper.DatabaseType defaultDatabaseType;
|
||||||
private String defaultArrayNumberTypeName, defaultArrayStringTypeName;
|
private String defaultArrayNumberTypeName, defaultArrayStringTypeName;
|
||||||
|
private SQLChecker sqlChecker;
|
||||||
private Set<String> allowedMaxRowParamNames;
|
private Set<String> allowedMaxRowParamNames;
|
||||||
private CompileTimeResultSetMapper rsm;
|
private CompileTimeResultSetMapper rsm;
|
||||||
|
|
||||||
@ -114,14 +117,25 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
cleanerElement = elements.getTypeElement(Cleaner.class.getCanonicalName());
|
cleanerElement = elements.getTypeElement(Cleaner.class.getCanonicalName());
|
||||||
cleanerType = types.getDeclaredType(cleanerElement, types.getWildcardType(null, null));
|
cleanerType = types.getDeclaredType(cleanerElement, types.getWildcardType(null, null));
|
||||||
|
|
||||||
final String databaseType = processingEnv.getOptions().get("JdbcMapper.databaseType");
|
enumType = types.getDeclaredType(elements.getTypeElement(Enum.class.getCanonicalName()), types.getWildcardType(null, null));
|
||||||
|
|
||||||
|
final String databaseType = processingEnv.getOptions().get("jdbcMapper.databaseType");
|
||||||
defaultDatabaseType = databaseType == null ? JdbcMapper.DatabaseType.STANDARD : JdbcMapper.DatabaseType.valueOf(databaseType);
|
defaultDatabaseType = databaseType == null ? JdbcMapper.DatabaseType.STANDARD : JdbcMapper.DatabaseType.valueOf(databaseType);
|
||||||
defaultArrayNumberTypeName = processingEnv.getOptions().get("JdbcMapper.arrayNumberTypeName");
|
defaultArrayNumberTypeName = processingEnv.getOptions().get("jdbcMapper.arrayNumberTypeName");
|
||||||
if (defaultArrayNumberTypeName == null || defaultArrayNumberTypeName.isEmpty())
|
if (defaultArrayNumberTypeName == null || defaultArrayNumberTypeName.isEmpty())
|
||||||
defaultArrayNumberTypeName = defaultDatabaseType.arrayNumberTypeName;
|
defaultArrayNumberTypeName = defaultDatabaseType.arrayNumberTypeName;
|
||||||
defaultArrayStringTypeName = processingEnv.getOptions().get("JdbcMapper.arrayStringTypeName");
|
defaultArrayStringTypeName = processingEnv.getOptions().get("jdbcMapper.arrayStringTypeName");
|
||||||
if (defaultArrayStringTypeName == null || defaultArrayStringTypeName.isEmpty())
|
if (defaultArrayStringTypeName == null || defaultArrayStringTypeName.isEmpty())
|
||||||
defaultArrayStringTypeName = defaultDatabaseType.arrayStringTypeName;
|
defaultArrayStringTypeName = defaultDatabaseType.arrayStringTypeName;
|
||||||
|
final String sqlCheckerClass = processingEnv.getOptions().get("jdbcMapper.sqlCheckerClass");
|
||||||
|
if(sqlCheckerClass != null) {
|
||||||
|
try {
|
||||||
|
sqlChecker = (SQLChecker) Class.forName(sqlCheckerClass).newInstance();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
|
||||||
|
"Error instantiating class specified by jdbcMapper.sqlCheckerClass, needs to implement SQLChecker and have a public no-arg constructor:" + toString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String allowedMaxRowParamNames = processingEnv.getOptions().get("JdbcMapper.allowedMaxRowParamNames");
|
String allowedMaxRowParamNames = processingEnv.getOptions().get("JdbcMapper.allowedMaxRowParamNames");
|
||||||
if (allowedMaxRowParamNames == null || allowedMaxRowParamNames.isEmpty())
|
if (allowedMaxRowParamNames == null || allowedMaxRowParamNames.isEmpty())
|
||||||
@ -162,6 +176,22 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
arrayStringTypeName = !mapper.arrayStringTypeName().isEmpty() ? mapper.arrayStringTypeName() :
|
arrayStringTypeName = !mapper.arrayStringTypeName().isEmpty() ? mapper.arrayStringTypeName() :
|
||||||
(mapper.databaseType() == defaultDatabaseType ? defaultArrayStringTypeName : mapper.databaseType().arrayStringTypeName);
|
(mapper.databaseType() == defaultDatabaseType ? defaultArrayStringTypeName : mapper.databaseType().arrayStringTypeName);
|
||||||
}
|
}
|
||||||
|
final ArrayInList arrayInList;
|
||||||
|
if(sqlChecker != null) {
|
||||||
|
switch(databaseType) {
|
||||||
|
case ORACLE:
|
||||||
|
arrayInList = new OracleArrayInList(arrayNumberTypeName, arrayStringTypeName);
|
||||||
|
break;
|
||||||
|
case STANDARD:
|
||||||
|
arrayInList = new ArrayInList(arrayNumberTypeName, arrayStringTypeName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// no support
|
||||||
|
arrayInList = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arrayInList = null;
|
||||||
|
}
|
||||||
final String sqlParserMirror = getSqlParser(mapper).toString();
|
final String sqlParserMirror = getSqlParser(mapper).toString();
|
||||||
//final SQLParser parser = new SimpleSQLParser();//(SQLParser)Class.forName(mapper.sqlParser().getCanonicalName()).newInstance();
|
//final SQLParser parser = new SimpleSQLParser();//(SQLParser)Class.forName(mapper.sqlParser().getCanonicalName()).newInstance();
|
||||||
//final SQLParser parser = mapper.sqlParser().equals(SQLParser.class) ? new SimpleSQLParser() : mapper.sqlParser().newInstance();
|
//final SQLParser parser = mapper.sqlParser().equals(SQLParser.class) ? new SimpleSQLParser() : mapper.sqlParser().newInstance();
|
||||||
@ -380,7 +410,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
w.write("conn.prepareStatement(");
|
w.write("conn.prepareStatement(");
|
||||||
}
|
}
|
||||||
w.write('"');
|
w.write('"');
|
||||||
w.write(sqlStatement.replace("\n", "\\n").replace("\"", "\\\""));
|
w.write(sqlStatement.replace("\"", "\\\"").replace("\n", "\\n\" +\n\t\t\t \""));
|
||||||
w.write("\");\n");
|
w.write("\");\n");
|
||||||
|
|
||||||
// now bind parameters
|
// now bind parameters
|
||||||
@ -439,6 +469,9 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
w.write("\t\t}\n");
|
w.write("\t\t}\n");
|
||||||
|
|
||||||
w.write("\t}\n");
|
w.write("\t}\n");
|
||||||
|
|
||||||
|
if(sqlChecker != null)
|
||||||
|
sqlChecker.checkSql(processingEnv, genClass, mapper, databaseType, eeMethod, sqlStatement, bindParams, arrayInList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// look on super classes and interfaces recursively
|
// look on super classes and interfaces recursively
|
||||||
@ -501,10 +534,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
tryClose(w);
|
tryClose(w);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
final StringWriter sw = new StringWriter();
|
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage() + toString(e), element);
|
||||||
sw.write('\n');
|
|
||||||
e.printStackTrace(new PrintWriter(sw));
|
|
||||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage() + sw.toString(), element);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -876,4 +906,20 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String toString(final Throwable e) {
|
||||||
|
StringWriter sw = null;
|
||||||
|
PrintWriter pw = null;
|
||||||
|
try {
|
||||||
|
sw = new StringWriter();
|
||||||
|
sw.write(String.format("%n"));
|
||||||
|
pw = new PrintWriter(sw);
|
||||||
|
e.printStackTrace(new PrintWriter(pw));
|
||||||
|
pw.flush();
|
||||||
|
return sw.toString();
|
||||||
|
} finally {
|
||||||
|
tryClose(sw);
|
||||||
|
tryClose(pw);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.moparisthebest.jdbc.codegen;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.ArrayInList;
|
||||||
|
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface SQLChecker {
|
||||||
|
void checkSql(final ProcessingEnvironment processingEnv, final TypeElement classElement, final JdbcMapper.Mapper mapper, final JdbcMapper.DatabaseType databaseType, final ExecutableElement method, final String sqlStatement, final List<VariableElement> bindParams, final ArrayInList arrayInList) throws SQLException;
|
||||||
|
}
|
@ -0,0 +1,179 @@
|
|||||||
|
package com.moparisthebest.jdbc.codegen;
|
||||||
|
|
||||||
|
import com.moparisthebest.jdbc.ArrayInList;
|
||||||
|
import com.moparisthebest.jdbc.QueryMapper;
|
||||||
|
|
||||||
|
import javax.annotation.processing.Messager;
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
|
import javax.lang.model.type.ArrayType;
|
||||||
|
import javax.lang.model.type.DeclaredType;
|
||||||
|
import javax.lang.model.type.TypeKind;
|
||||||
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
import javax.tools.Diagnostic;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.moparisthebest.jdbc.TryClose.tryClose;
|
||||||
|
import static com.moparisthebest.jdbc.codegen.JdbcMapperProcessor.*;
|
||||||
|
|
||||||
|
public class SimpleSQLChecker implements SQLChecker {
|
||||||
|
@Override
|
||||||
|
public void checkSql(final ProcessingEnvironment processingEnv, final TypeElement classElement, final JdbcMapper.Mapper mapper, final JdbcMapper.DatabaseType databaseType, final ExecutableElement method, final String sqlStatement, final List<VariableElement> bindParams, final ArrayInList arrayInList) throws SQLException {
|
||||||
|
Connection conn = null;
|
||||||
|
QueryMapper qm = null;
|
||||||
|
try {
|
||||||
|
conn = getConnection(classElement, mapper, databaseType);
|
||||||
|
if (conn == null) {
|
||||||
|
getMessager().printMessage(Diagnostic.Kind.ERROR, "connection must be non-null for testing", classElement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
qm = new QueryMapper(conn);
|
||||||
|
qm.executeUpdate(getSqlToExecute(classElement, conn, databaseType, sqlStatement), getFakeBindParams(bindParams, conn, arrayInList));
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleException(getMessager(), e, classElement, method);
|
||||||
|
} finally {
|
||||||
|
if (conn != null) {
|
||||||
|
try {
|
||||||
|
conn.rollback();
|
||||||
|
conn.setAutoCommit(true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
tryClose(conn);
|
||||||
|
}
|
||||||
|
tryClose(qm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnection(final TypeElement classElement, final JdbcMapper.Mapper mapper, final JdbcMapper.DatabaseType databaseType) throws SQLException {
|
||||||
|
if (mapper.jndiName().isEmpty()) {
|
||||||
|
throw new RuntimeException("@JdbcMapper.jndiName must be non-null for testing");
|
||||||
|
}
|
||||||
|
return JdbcMapperFactory.connectionFactory(mapper.jndiName()).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSqlToExecute(final TypeElement classElement, final Connection conn, final JdbcMapper.DatabaseType databaseType, final String sqlStatement) {
|
||||||
|
switch (databaseType) {
|
||||||
|
case ORACLE:
|
||||||
|
// oracle, being terrible, gives this error message for explain plans on MERGES
|
||||||
|
// so we will just execute those directly instead...
|
||||||
|
// ORA-00600: internal error code, arguments: [qctfrc : bfc], [22], [0], [5], [1], [1], [2], [371], [], [], [], []
|
||||||
|
// we are assuming length is at least 5 after being trimmed, if anyone can come up with a valid SQL statement that is shorter let me know...
|
||||||
|
if (sqlStatement.trim().substring(0, 5).toUpperCase().equals("MERGE"))
|
||||||
|
return sqlStatement;
|
||||||
|
return "EXPLAIN PLAN FOR " + sqlStatement;
|
||||||
|
case STANDARD:
|
||||||
|
return "EXPLAIN " + sqlStatement;
|
||||||
|
}
|
||||||
|
return sqlStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleException(final Messager messager, final Exception e, final TypeElement classElement, final ExecutableElement method) {
|
||||||
|
if (e.getMessage().startsWith("ORA-02291: integrity constraint"))
|
||||||
|
return; // since we make up values and rollback we can ignore this, it means SQL is correct at least
|
||||||
|
getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage(), method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Object> getFakeBindParams(final List<VariableElement> bindParams, final Connection conn, final ArrayInList arrayInList) throws SQLException {
|
||||||
|
final Collection<Object> ret = new ArrayList<Object>(bindParams.size());
|
||||||
|
for (final VariableElement bindParam : bindParams) {
|
||||||
|
ret.add(getFakeBindParam(bindParam, conn, arrayInList));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String defaultString = "n"; // oracle considers empty strings to be null...
|
||||||
|
|
||||||
|
public Object getFakeBindParam(final VariableElement param, final Connection conn, final ArrayInList arrayInList) throws SQLException {
|
||||||
|
final TypeMirror o = param.asType();
|
||||||
|
// special behavior
|
||||||
|
if (param instanceof InListVariableElement) {
|
||||||
|
final TypeMirror componentType;
|
||||||
|
if (o.getKind() == TypeKind.ARRAY) {
|
||||||
|
componentType = ((ArrayType) o).getComponentType();
|
||||||
|
} else if (o.getKind() == TypeKind.DECLARED && types.isAssignable(o, collectionType)) {
|
||||||
|
final DeclaredType dt = (DeclaredType) o;
|
||||||
|
componentType = dt.getTypeArguments().get(0);
|
||||||
|
} else {
|
||||||
|
getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "invalid in-list-variable type, returning null", ((InListVariableElement) param).delegate);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (types.isAssignable(componentType, numberType)) {
|
||||||
|
return arrayInList.toArray(conn, true, new Long[]{0L}); // todo: not quite right, oh well for now
|
||||||
|
} else if (types.isAssignable(componentType, stringType) || types.isAssignable(componentType, enumType)) {
|
||||||
|
return arrayInList.toArray(conn, false, new String[]{defaultString});
|
||||||
|
} else {
|
||||||
|
getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "invalid in-list-variable type, returning null", ((InListVariableElement) param).delegate);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final JdbcMapper.Blob blob = param.getAnnotation(JdbcMapper.Blob.class);
|
||||||
|
if (blob != null) {
|
||||||
|
return new ByteArrayInputStream(new byte[1]);
|
||||||
|
} else {
|
||||||
|
final JdbcMapper.Clob clob = param.getAnnotation(JdbcMapper.Clob.class);
|
||||||
|
if (clob != null) {
|
||||||
|
return new StringReader(defaultString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end special behavior
|
||||||
|
// we are going to put most common ones up top so it should execute faster normally
|
||||||
|
if (types.isAssignable(o, stringType)) {
|
||||||
|
return defaultString;
|
||||||
|
} else if (o.getKind().isPrimitive() || types.isAssignable(o, numberType)) {
|
||||||
|
switch (o.getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return true;
|
||||||
|
case BYTE:
|
||||||
|
case SHORT:
|
||||||
|
case INT:
|
||||||
|
return 0;
|
||||||
|
case LONG:
|
||||||
|
return 0L;
|
||||||
|
case CHAR:
|
||||||
|
return ' ';
|
||||||
|
case FLOAT:
|
||||||
|
return 0F;
|
||||||
|
case DOUBLE:
|
||||||
|
return 0D;
|
||||||
|
}
|
||||||
|
return null;//new BigDecimal(0D); // todo: not quite right, maybe good enough
|
||||||
|
// java.util.Date support, put it in a Timestamp
|
||||||
|
} else if (types.isAssignable(o, utilDateType)) {
|
||||||
|
return new java.sql.Timestamp(0);
|
||||||
|
}
|
||||||
|
//IFJAVA8_START
|
||||||
|
else if (types.isAssignable(o, instantType) || types.isAssignable(o, localDateTimeType) || types.isAssignable(o, zonedDateTimeType) || types.isAssignable(o, offsetDateTimeType)) {
|
||||||
|
return new java.sql.Timestamp(0);
|
||||||
|
} else if (types.isAssignable(o, localDateType)) {
|
||||||
|
return new java.sql.Date(0);
|
||||||
|
} else if (types.isAssignable(o, localTimeType) || types.isAssignable(o, offsetTimeType)) {
|
||||||
|
return new java.sql.Time(0);
|
||||||
|
}
|
||||||
|
//IFJAVA8_END
|
||||||
|
// CLOB support
|
||||||
|
else if (types.isAssignable(o, readerType) || types.isAssignable(o, clobType)) {
|
||||||
|
return new StringReader(defaultString);
|
||||||
|
} else if (types.isAssignable(o, inputStreamType) || types.isAssignable(o, blobType) || types.isAssignable(o, fileType)) {
|
||||||
|
return new ByteArrayInputStream(new byte[1]);
|
||||||
|
} else if (types.isAssignable(o, byteArrayType)) {
|
||||||
|
return new byte[1];
|
||||||
|
} else if (types.isAssignable(o, sqlArrayType)) {
|
||||||
|
getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "java.sql.Array not yet supported for getFakeBindParam, returning null", param);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
// shouldn't get here ever, if we do the types should be more specific
|
||||||
|
getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "unsupported type for getFakeBindParam, returning null", param);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,18 +16,31 @@ public class ArrayInList implements InList {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ArrayInList() {
|
protected final String numericType, otherType;
|
||||||
|
|
||||||
|
public ArrayInList(final String numericType, final String otherType) {
|
||||||
|
this.numericType = numericType;
|
||||||
|
this.otherType = otherType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayInList() {
|
||||||
|
this("number", "text");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String columnAppend(final String columnName) {
|
protected String columnAppend(final String columnName) {
|
||||||
return "(" + columnName + " = ANY(?))";
|
return "(" + columnName + " = ANY(?))";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Array toArray(final Connection conn, final String typeName, final Object[] elements) throws SQLException {
|
||||||
|
return conn.createArrayOf(typeName, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array toArray(final Connection conn, final boolean numeric, final Object[] elements) throws SQLException {
|
||||||
|
return toArray(conn, numeric ? numericType : otherType, elements);
|
||||||
|
}
|
||||||
|
|
||||||
protected <T> Array toArray(final Connection conn, final Collection<T> values) throws SQLException {
|
protected <T> Array toArray(final Connection conn, final Collection<T> values) throws SQLException {
|
||||||
return conn.createArrayOf(
|
return toArray(conn, values.iterator().next() instanceof Number, values.toArray());
|
||||||
values.iterator().next() instanceof Number ? "number" : "text",
|
|
||||||
values.toArray()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> InListObject inList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException {
|
public <T> InListObject inList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException {
|
||||||
|
@ -34,25 +34,22 @@ public class OracleArrayInList extends ArrayInList {
|
|||||||
createArray = ca;
|
createArray = ca;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OracleArrayInList() {
|
public OracleArrayInList(final String numberType, final String otherType) {
|
||||||
|
super(numberType, otherType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OracleArrayInList() {
|
||||||
|
this("ARRAY_NUM_TYPE", "ARRAY_STR_TYPE");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String columnAppend(final String columnName) {
|
protected String columnAppend(final String columnName) {
|
||||||
return "(" + columnName + " IN(select column_value from table(?)))";
|
return "(" + columnName + " IN(select column_value from table(?)))";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> Array toArray(final Connection conn, final Collection<T> values) throws SQLException {
|
public Array toArray(final Connection conn, final String typeName, final Object[] elements) throws SQLException {
|
||||||
/*
|
//return conn.unwrap(oracle.jdbc.OracleConnection.class).createOracleArray(typeName, elements);
|
||||||
return conn.unwrap(oracle.jdbc.OracleConnection.class).createOracleArray(
|
|
||||||
values.iterator().next() instanceof Number ? "ARRAY_NUM_TYPE" : "ARRAY_STR_TYPE",
|
|
||||||
values.toArray()
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
try {
|
try {
|
||||||
return (Array) createArray.invoke(conn.unwrap(oracleConnection),
|
return (Array) createArray.invoke(conn.unwrap(oracleConnection), typeName, elements);
|
||||||
values.iterator().next() instanceof Number ? "ARRAY_NUM_TYPE" : "ARRAY_STR_TYPE",
|
|
||||||
values.toArray()
|
|
||||||
);
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -179,6 +179,8 @@ public class QueryMapper implements JdbcMapper {
|
|||||||
ps.setBlob(index, (java.sql.Blob) o);
|
ps.setBlob(index, (java.sql.Blob) o);
|
||||||
else if (o instanceof ArrayInList.ArrayListObject)
|
else if (o instanceof ArrayInList.ArrayListObject)
|
||||||
ps.setArray(index, ((ArrayInList.ArrayListObject) o).getArray());
|
ps.setArray(index, ((ArrayInList.ArrayListObject) o).getArray());
|
||||||
|
else if (o instanceof java.sql.Array)
|
||||||
|
ps.setArray(index, (java.sql.Array) o);
|
||||||
else
|
else
|
||||||
ps.setObject(index, o); // probably won't get here ever, but just in case...
|
ps.setObject(index, o); // probably won't get here ever, but just in case...
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user