mirror of
https://github.com/moparisthebest/JdbcMapper
synced 2024-11-27 19:32:19 -05:00
Move InList and PreparedStatementUtil up to common, add support for custom sql/runtime bind in JdbcMapper
This commit is contained in:
parent
5e7bf3e0e2
commit
26617e0781
@ -55,29 +55,16 @@ public class ArrayInList implements InList {
|
||||
}
|
||||
|
||||
public <T> InListObject inList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException {
|
||||
return values == null || values.isEmpty() ? InListObject.inEmpty : new ArrayListObject(
|
||||
return values == null || values.isEmpty() ? InListObject.inEmpty : new InListObject(
|
||||
columnAppendIn(columnName),
|
||||
toArray(conn, values)
|
||||
);
|
||||
}
|
||||
|
||||
public <T> InListObject notInList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException {
|
||||
return values == null || values.isEmpty() ? InListObject.notInEmpty : new ArrayListObject(
|
||||
return values == null || values.isEmpty() ? InListObject.notInEmpty : new InListObject(
|
||||
columnAppendNotIn(columnName),
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -34,29 +34,16 @@ public class BindInList implements InList {
|
||||
}
|
||||
|
||||
public <T> InListObject inList(final Connection conn, final String columnName, final Collection<T> values) {
|
||||
return values == null || values.isEmpty() ? InListObject.inEmpty : new BindInListObject(
|
||||
return values == null || values.isEmpty() ? InListObject.inEmpty : new InListObject(
|
||||
toInList(columnName, values, this.maxSize),
|
||||
values.toArray()
|
||||
values
|
||||
);
|
||||
}
|
||||
|
||||
public <T> InListObject notInList(final Connection conn, final String columnName, final Collection<T> values) {
|
||||
return values == null || values.isEmpty() ? InListObject.notInEmpty : new BindInListObject(
|
||||
return values == null || values.isEmpty() ? InListObject.notInEmpty : new InListObject(
|
||||
toNotInList(columnName, values, this.maxSize),
|
||||
values.toArray()
|
||||
values
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
108
common/src/main/java/com/moparisthebest/jdbc/InList.java
Normal file
108
common/src/main/java/com/moparisthebest/jdbc/InList.java
Normal file
@ -0,0 +1,108 @@
|
||||
package com.moparisthebest.jdbc;
|
||||
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
||||
import com.moparisthebest.jdbc.util.Bindable;
|
||||
import com.moparisthebest.jdbc.util.InListUtil;
|
||||
import com.moparisthebest.jdbc.util.PreparedStatementUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
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 {
|
||||
|
||||
InList defaultInList = InListObject.getDefaultInListInstance();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param values values for in list
|
||||
* @return object
|
||||
*/
|
||||
public <T> InListObject inList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException;
|
||||
|
||||
/**
|
||||
* 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 not in list
|
||||
* @return object
|
||||
*/
|
||||
public <T> InListObject notInList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException;
|
||||
|
||||
class InListObject implements Bindable {
|
||||
static final InListObject inEmpty = new InListObject(InListUtil.inEmpty, null);
|
||||
static final InListObject notInEmpty = new InListObject(InListUtil.notInEmpty, null);
|
||||
|
||||
public static InListObject inEmpty() {
|
||||
return inEmpty;
|
||||
}
|
||||
|
||||
public static InListObject notInEmpty() {
|
||||
return notInEmpty;
|
||||
}
|
||||
|
||||
private final String sql;
|
||||
private final Object bindObject;
|
||||
|
||||
public InListObject(final String sql, final Object bindObject) {
|
||||
this.sql = sql;
|
||||
this.bindObject = bindObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBindObject() {
|
||||
return bindObject;
|
||||
}
|
||||
|
||||
private static InList getDefaultInListInstance() {
|
||||
try {
|
||||
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"));
|
||||
return (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")) {
|
||||
return OptimalInList.instance();
|
||||
} else {
|
||||
switch (JdbcMapper.DatabaseType.valueOf(type)) {
|
||||
case DEFAULT:
|
||||
case BIND:
|
||||
return BindInList.instance();
|
||||
case ANY:
|
||||
return ArrayInList.instance();
|
||||
case ORACLE:
|
||||
return OracleArrayInList.instance();
|
||||
case UNNEST:
|
||||
return UnNestArrayInList.instance();
|
||||
default:
|
||||
throw new RuntimeException("Invalid queryMapper.databaseType: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// NEVER ignore
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.moparisthebest.jdbc.codegen;
|
||||
|
||||
import com.moparisthebest.jdbc.util.SqlBuilder;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -14,6 +16,14 @@ public interface JdbcMapper extends Closeable {
|
||||
|
||||
Connection getConnection();
|
||||
|
||||
//IFJAVA8_START
|
||||
|
||||
default SqlBuilder sqlBuilder() {
|
||||
return SqlBuilder.of(getConnection()); // todo: should this use the current inList ?
|
||||
}
|
||||
|
||||
//IFJAVA8_END
|
||||
|
||||
@Override
|
||||
void close();
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.moparisthebest.jdbc.util;
|
||||
|
||||
import static com.moparisthebest.jdbc.util.PreparedStatementUtil.noBind;
|
||||
|
||||
public interface Bindable {
|
||||
|
||||
Bindable empty = new Bindable() {
|
||||
@Override
|
||||
public Object getBindObject() {
|
||||
return noBind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This returns raw SQL to be included in a query, can contain bind params as standard ?
|
||||
* @return
|
||||
*/
|
||||
String toString();
|
||||
|
||||
/**
|
||||
* This returns an object (or list of objects, or list of list etc) to bind to the SQL snippet returned by toString()
|
||||
* PreparedStatementUtil must know how to bind this
|
||||
* @return
|
||||
*/
|
||||
Object getBindObject();
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
package com.moparisthebest.jdbc.util;
|
||||
|
||||
import com.moparisthebest.jdbc.InList;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
//IFJAVA8_START
|
||||
import java.time.*;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
//IFJAVA8_END
|
||||
|
||||
public class PreparedStatementUtil {
|
||||
|
||||
public static final Object noBind = new Object();
|
||||
|
||||
public static PreparedStatement bind(final PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
recursiveBind(ps, bindObjects);
|
||||
return ps;
|
||||
}
|
||||
|
||||
public static void setObject(final PreparedStatement ps, final int index, final Object o) throws SQLException {
|
||||
// we are going to put most common ones up top so it should execute faster normally
|
||||
if (o == null || o instanceof String || o instanceof Number)
|
||||
ps.setObject(index, o);
|
||||
// java.util.Date support, put it in a Timestamp
|
||||
else if (o instanceof java.util.Date)
|
||||
ps.setObject(index, o.getClass().equals(java.util.Date.class) ? new java.sql.Timestamp(((java.util.Date)o).getTime()) : o);
|
||||
//IFJAVA8_START// todo: other java.time types
|
||||
else if (o instanceof Instant)
|
||||
ps.setObject(index, java.sql.Timestamp.from((Instant)o));
|
||||
else if (o instanceof LocalDateTime)
|
||||
ps.setObject(index, java.sql.Timestamp.valueOf((LocalDateTime)o));
|
||||
else if (o instanceof LocalDate)
|
||||
ps.setObject(index, java.sql.Date.valueOf((LocalDate)o));
|
||||
else if (o instanceof LocalTime)
|
||||
ps.setObject(index, java.sql.Time.valueOf((LocalTime)o));
|
||||
else if (o instanceof ZonedDateTime)
|
||||
ps.setObject(index, java.sql.Timestamp.from(((ZonedDateTime)o).toInstant()));
|
||||
else if (o instanceof OffsetDateTime)
|
||||
ps.setObject(index, java.sql.Timestamp.from(((OffsetDateTime)o).toInstant()));
|
||||
else if (o instanceof OffsetTime)
|
||||
ps.setObject(index, java.sql.Time.valueOf(((OffsetTime)o).toLocalTime())); // todo: no timezone?
|
||||
|
||||
//IFJAVA8_END
|
||||
// CLOB support
|
||||
else if (o instanceof Reader)
|
||||
ps.setClob(index, (Reader) o);
|
||||
else if (o instanceof ClobString)
|
||||
ps.setClob(index, ((ClobString) o).s == null ? null : new StringReader(((ClobString) o).s));
|
||||
else if (o instanceof java.sql.Clob)
|
||||
ps.setClob(index, (java.sql.Clob) o);
|
||||
// BLOB support
|
||||
else if (o instanceof byte[])
|
||||
ps.setBlob(index, new ByteArrayInputStream((byte[]) o));
|
||||
else if (o instanceof InputStream)
|
||||
ps.setBlob(index, (InputStream) o);
|
||||
else if (o instanceof File)
|
||||
try {
|
||||
ps.setBlob(index, new FileInputStream((File) o)); // todo: does this close this or leak a file descriptor?
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new SQLException("File to Blob FileNotFoundException", e);
|
||||
}
|
||||
else if (o instanceof BlobString)
|
||||
ps.setBlob(index, ((BlobString) o).s == null ? null : new ByteArrayInputStream(((BlobString) o).s.getBytes(((BlobString) o).charset)));
|
||||
else if (o instanceof java.sql.Blob)
|
||||
ps.setBlob(index, (java.sql.Blob) o);
|
||||
else if (o instanceof java.sql.Array)
|
||||
ps.setArray(index, (java.sql.Array) o);
|
||||
else if (o instanceof Enum)
|
||||
ps.setObject(index, ((Enum)o).name());
|
||||
else
|
||||
ps.setObject(index, o); // probably won't get here ever, but just in case...
|
||||
/*
|
||||
switch(ps.getParameterMetaData().getParameterType(index)){ // 'java.sql.SQLException: Unsupported feature', fully JDBC 3.0 compliant my ass, freaking oracle...
|
||||
case Types.CLOB:
|
||||
if(o instanceof String)
|
||||
ps.setObject(index, o);
|
||||
else if (o instanceof Reader)
|
||||
ps.setClob(index, (Reader) o);
|
||||
else if (o instanceof Clob)
|
||||
ps.setClob(index, (Clob) o);
|
||||
return;
|
||||
case Types.BLOB:
|
||||
if (o instanceof byte[])
|
||||
ps.setBlob(index, new ByteArrayInputStream((byte[])o));
|
||||
else if (o instanceof InputStream)
|
||||
ps.setBlob(index, (InputStream) o);
|
||||
else if (o instanceof File)
|
||||
try {
|
||||
ps.setBlob(index, new FileInputStream((File) o));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new SQLException("File to Blob FileNotFoundException", e);
|
||||
}
|
||||
else if (o instanceof Blob)
|
||||
ps.setBlob(index, (Blob) o);
|
||||
else if(o instanceof String)
|
||||
try{
|
||||
ps.setBlob(index, new ByteArrayInputStream(((String) o).getBytes("UTF-8")));
|
||||
}catch(UnsupportedEncodingException e){
|
||||
throw new SQLException("String to Blob UnsupportedEncodingException", e);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
ps.setObject(index, o);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public static int recursiveBind(final PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
return recursiveBindIndex(ps, 0, bindObjects);
|
||||
}
|
||||
|
||||
public static int recursiveBindIndex(final PreparedStatement ps, int index, final Object... bindObjects) throws SQLException {
|
||||
if (bindObjects != null && bindObjects.length > 0) {
|
||||
for (final Object o : bindObjects) {
|
||||
if (o != null) {
|
||||
if (o == InList.InListObject.inEmpty() || o == InList.InListObject.notInEmpty() || o == noBind) {
|
||||
continue; // ignore
|
||||
} else if (o instanceof Bindable) {
|
||||
index = recursiveBindIndex(ps, index, ((Bindable) o).getBindObject());
|
||||
continue;
|
||||
} else if (o instanceof Object[]) {
|
||||
index = recursiveBindIndex(ps, index, (Object[]) o);
|
||||
continue;
|
||||
} else if (o instanceof Collection) {
|
||||
// is creating 1 array and doing 1 method call faster than falling through to iterator and making multiple method calls/arrays? *probably* ?
|
||||
index = recursiveBindIndex(ps, index, ((Collection) o).toArray());
|
||||
continue;
|
||||
} else if(o instanceof Iterable) {
|
||||
for(final Object o2 : (Iterable) o) {
|
||||
index = recursiveBindIndex(ps, index, o2);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//System.out.printf("index: '%d' bound to '%s'\n", index+1, o);
|
||||
setObject(ps, ++index, o);
|
||||
//ps.setObject(++index, o);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public static Object wrapClob(String s) {
|
||||
return new ClobString(s);
|
||||
}
|
||||
|
||||
public static Object wrapBlob(String s) {
|
||||
return new BlobString(s, UTF_8);
|
||||
}
|
||||
|
||||
public static Object wrapBlob(final String s, final Charset charset) {
|
||||
return new BlobString(s, charset == null ? UTF_8 : charset);
|
||||
}
|
||||
|
||||
private static class StringWrapper {
|
||||
public final String s;
|
||||
|
||||
private StringWrapper(String s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return s;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof StringWrapper)) return false;
|
||||
StringWrapper that = (StringWrapper) o;
|
||||
return !(s != null ? !s.equals(that.s) : that.s != null);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return s != null ? s.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClobString extends StringWrapper {
|
||||
private ClobString(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BlobString extends StringWrapper {
|
||||
private final Charset charset;
|
||||
private BlobString(final String s, final Charset charset) {
|
||||
super(s);
|
||||
this.charset = charset;
|
||||
}
|
||||
}
|
||||
|
||||
/*IFJAVA6_START
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
IFJAVA6_END*/
|
||||
}
|
@ -0,0 +1,463 @@
|
||||
package com.moparisthebest.jdbc.util;
|
||||
|
||||
import com.moparisthebest.jdbc.*;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
//IFJAVA8_START
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
//IFJAVA8_END
|
||||
|
||||
import static com.moparisthebest.jdbc.InList.defaultInList;
|
||||
|
||||
public class SqlBuilder implements Appendable, CharSequence, Collection<Object>, Bindable {
|
||||
|
||||
private final StringBuilder sb;
|
||||
private final Collection<Object> bindObjects;
|
||||
|
||||
private final InList inList;
|
||||
private final Connection conn;
|
||||
|
||||
private SqlBuilder(final Connection conn, final InList inList, final StringBuilder sb, final Collection<Object> bindObjects) {
|
||||
if(sb == null || bindObjects == null || inList == null || conn == null)
|
||||
throw new NullPointerException("all arguments must be non-null");
|
||||
this.sb = sb;
|
||||
this.bindObjects = bindObjects;
|
||||
this.inList = inList.instance(conn);
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
public static SqlBuilder of(final Connection conn, final InList inList, final StringBuilder sb, final Collection<Object> bindObjects) {
|
||||
return new SqlBuilder(conn, inList, sb, bindObjects);
|
||||
}
|
||||
|
||||
public static SqlBuilder of(final Connection conn) {
|
||||
return new SqlBuilder(conn, defaultInList, new StringBuilder(), new ArrayList<Object>());
|
||||
}
|
||||
|
||||
public static SqlBuilder of(final Connection conn, final Collection<Object> bindObjects) {
|
||||
return new SqlBuilder(conn, defaultInList, new StringBuilder(), bindObjects);
|
||||
}
|
||||
|
||||
public static SqlBuilder of(final Connection conn, final StringBuilder sb) {
|
||||
return new SqlBuilder(conn, defaultInList, sb, new ArrayList<Object>());
|
||||
}
|
||||
|
||||
public static SqlBuilder of(final Connection conn, final InList inList) {
|
||||
return new SqlBuilder(conn, inList, new StringBuilder(), new ArrayList<Object>());
|
||||
}
|
||||
|
||||
// start custom SqlBuilder methods
|
||||
|
||||
public SqlBuilder append(final String sql, final Object bindObject) {
|
||||
sb.append(sql);
|
||||
this.bindObjects.add(bindObject);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(final String sql, final Object... bindObjects) {
|
||||
return this.append(sql, (Object) bindObjects);
|
||||
}
|
||||
|
||||
public <T> SqlBuilder appendInList(final String columnName, final Collection<T> values) throws SQLException {
|
||||
final InList.InListObject inListObject = inList.inList(conn, columnName, values);
|
||||
return this.append(inListObject.toString(), inListObject.getBindObject());
|
||||
}
|
||||
|
||||
public <T> SqlBuilder appendNotInList(final String columnName, final Collection<T> values) throws SQLException {
|
||||
final InList.InListObject inListObject = inList.notInList(conn, columnName, values);
|
||||
return this.append(inListObject.toString(), inListObject.getBindObject());
|
||||
}
|
||||
|
||||
public StringBuilder getStringBuilder() {
|
||||
return sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getBindObject() {
|
||||
return bindObjects;
|
||||
}
|
||||
|
||||
public InList getInList() {
|
||||
return inList;
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return conn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof SqlBuilder)) return false;
|
||||
|
||||
final SqlBuilder that = (SqlBuilder) o;
|
||||
|
||||
return bindObjects.equals(that.bindObjects) && sb.toString().equals(that.sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = sb.toString().hashCode();
|
||||
result = 31 * result + bindObjects.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
// end custom SqlBuilder methods
|
||||
// start StringBuilder delegates
|
||||
|
||||
public SqlBuilder append(Object obj) {
|
||||
sb.append(obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(String str) {
|
||||
sb.append(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(StringBuffer sb) {
|
||||
this.sb.append(sb);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlBuilder append(CharSequence s) {
|
||||
sb.append(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlBuilder append(CharSequence s, int start, int end) {
|
||||
sb.append(s, start, end);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(char[] str) {
|
||||
sb.append(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(char[] str, int offset, int len) {
|
||||
sb.append(str, offset, len);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(boolean b) {
|
||||
sb.append(b);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlBuilder append(char c) {
|
||||
sb.append(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(int i) {
|
||||
sb.append(i);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(long lng) {
|
||||
sb.append(lng);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(float f) {
|
||||
sb.append(f);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder append(double d) {
|
||||
sb.append(d);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder appendCodePoint(int codePoint) {
|
||||
sb.appendCodePoint(codePoint);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder delete(int start, int end) {
|
||||
sb.delete(start, end);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder deleteCharAt(int index) {
|
||||
sb.deleteCharAt(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder replace(int start, int end, String str) {
|
||||
sb.replace(start, end, str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int index, char[] str, int offset, int len) {
|
||||
sb.insert(index, str, offset, len);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, Object obj) {
|
||||
sb.insert(offset, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, String str) {
|
||||
sb.insert(offset, str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, char[] str) {
|
||||
sb.insert(offset, str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int dstOffset, CharSequence s) {
|
||||
sb.insert(dstOffset, s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int dstOffset, CharSequence s, int start, int end) {
|
||||
sb.insert(dstOffset, s, start, end);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, boolean b) {
|
||||
sb.insert(offset, b);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, char c) {
|
||||
sb.insert(offset, c);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, int i) {
|
||||
sb.insert(offset, i);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, long l) {
|
||||
sb.insert(offset, l);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, float f) {
|
||||
sb.insert(offset, f);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SqlBuilder insert(int offset, double d) {
|
||||
sb.insert(offset, d);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int indexOf(String str) {
|
||||
return sb.indexOf(str);
|
||||
}
|
||||
|
||||
public int indexOf(String str, int fromIndex) {
|
||||
return sb.indexOf(str, fromIndex);
|
||||
}
|
||||
|
||||
public int lastIndexOf(String str) {
|
||||
return sb.lastIndexOf(str);
|
||||
}
|
||||
|
||||
public int lastIndexOf(String str, int fromIndex) {
|
||||
return sb.lastIndexOf(str, fromIndex);
|
||||
}
|
||||
|
||||
public SqlBuilder reverse() {
|
||||
sb.reverse();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return sb.length();
|
||||
}
|
||||
|
||||
public int capacity() {
|
||||
return sb.capacity();
|
||||
}
|
||||
|
||||
public void ensureCapacity(int minimumCapacity) {
|
||||
sb.ensureCapacity(minimumCapacity);
|
||||
}
|
||||
|
||||
public void trimToSize() {
|
||||
sb.trimToSize();
|
||||
}
|
||||
|
||||
public void setLength(int newLength) {
|
||||
sb.setLength(newLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return sb.charAt(index);
|
||||
}
|
||||
|
||||
public int codePointAt(int index) {
|
||||
return sb.codePointAt(index);
|
||||
}
|
||||
|
||||
public int codePointBefore(int index) {
|
||||
return sb.codePointBefore(index);
|
||||
}
|
||||
|
||||
public int codePointCount(int beginIndex, int endIndex) {
|
||||
return sb.codePointCount(beginIndex, endIndex);
|
||||
}
|
||||
|
||||
public int offsetByCodePoints(int index, int codePointOffset) {
|
||||
return sb.offsetByCodePoints(index, codePointOffset);
|
||||
}
|
||||
|
||||
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
|
||||
sb.getChars(srcBegin, srcEnd, dst, dstBegin);
|
||||
}
|
||||
|
||||
public void setCharAt(int index, char ch) {
|
||||
sb.setCharAt(index, ch);
|
||||
}
|
||||
|
||||
public String substring(int start) {
|
||||
return sb.substring(start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return sb.subSequence(start, end);
|
||||
}
|
||||
|
||||
public String substring(int start, int end) {
|
||||
return sb.substring(start, end);
|
||||
}
|
||||
|
||||
//IFJAVA8_START
|
||||
|
||||
@Override
|
||||
public IntStream chars() {
|
||||
return sb.chars();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntStream codePoints() {
|
||||
return sb.codePoints();
|
||||
}
|
||||
|
||||
//IFJAVA8_END
|
||||
|
||||
// end StringBuilder delegates
|
||||
// start Collection delegates
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return bindObjects.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return bindObjects.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return bindObjects.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return bindObjects.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return bindObjects.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return bindObjects.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Object e) {
|
||||
return bindObjects.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return bindObjects.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return bindObjects.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Object> c) {
|
||||
return bindObjects.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return bindObjects.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return bindObjects.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
bindObjects.clear();
|
||||
}
|
||||
|
||||
//IFJAVA8_START
|
||||
|
||||
@Override
|
||||
public boolean removeIf(Predicate<? super Object> filter) {
|
||||
return bindObjects.removeIf(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<Object> spliterator() {
|
||||
return bindObjects.spliterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Object> stream() {
|
||||
return bindObjects.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Object> parallelStream() {
|
||||
return bindObjects.parallelStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super Object> action) {
|
||||
bindObjects.forEach(action);
|
||||
}
|
||||
|
||||
//IFJAVA8_END
|
||||
|
||||
// end Collection delegates
|
||||
}
|
@ -3,6 +3,7 @@ package com.moparisthebest.jdbc.codegen;
|
||||
import com.moparisthebest.jdbc.*;
|
||||
import com.moparisthebest.jdbc.codegen.spring.SpringRepository;
|
||||
import com.moparisthebest.jdbc.codegen.spring.SpringScope;
|
||||
import com.moparisthebest.jdbc.util.Bindable;
|
||||
|
||||
import javax.annotation.processing.*;
|
||||
import javax.lang.model.SourceVersion;
|
||||
@ -28,6 +29,7 @@ import java.util.stream.Stream;
|
||||
import static com.moparisthebest.jdbc.TryClose.tryClose;
|
||||
import static com.moparisthebest.jdbc.codegen.JdbcMapper.DatabaseType.ORACLE;
|
||||
import static com.moparisthebest.jdbc.codegen.JdbcMapperFactory.SUFFIX;
|
||||
import static com.moparisthebest.jdbc.codegen.SpecialVariableElement.SpecialType.SQL;
|
||||
|
||||
/**
|
||||
* Created by mopar on 5/24/17.
|
||||
@ -36,7 +38,7 @@ import static com.moparisthebest.jdbc.codegen.JdbcMapperFactory.SUFFIX;
|
||||
@SupportedOptions({"jdbcMapper.databaseType", "jdbcMapper.arrayNumberTypeName", "jdbcMapper.arrayStringTypeName", "jdbcMapper.allowedMaxRowParamNames", "jdbcMapper.sqlCheckerClass"})
|
||||
public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
|
||||
public static final Pattern paramPattern = Pattern.compile("\\{(([^\\s]+)\\s+(([Nn][Oo][Tt]\\s+)?[Ii][Nn]\\s+))?([BbCc][Ll][Oo][Bb]\\s*:\\s*([^:}]+\\s*:\\s*)?)?([^}]+)\\}");
|
||||
public static final Pattern paramPattern = Pattern.compile("\\{(([^\\s]+)\\s+(([Nn][Oo][Tt]\\s+)?[Ii][Nn]\\s+))?([BbCcSs][LlQq][OoLl][Bb]?\\s*:\\s*([^:}]+\\s*:\\s*)?)?([^}]+)\\}");
|
||||
|
||||
public static final SourceVersion RELEASE_8;
|
||||
public static boolean java8;
|
||||
@ -64,11 +66,10 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
static TypeMirror sqlExceptionType, stringType, numberType, utilDateType, readerType, clobType, connectionType, jdbcMapperType,
|
||||
byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, calendarType, cleanerType, enumType;
|
||||
byteArrayType, inputStreamType, fileType, blobType, sqlArrayType, collectionType, iterableType, bindableType, calendarType, cleanerType, enumType;
|
||||
//IFJAVA8_START
|
||||
static TypeMirror streamType, instantType, localDateTimeType, localDateType, localTimeType, zonedDateTimeType, offsetDateTimeType, offsetTimeType;
|
||||
//IFJAVA8_END
|
||||
private TypeElement cleanerElement;
|
||||
private JdbcMapper.DatabaseType defaultDatabaseType;
|
||||
private String defaultArrayNumberTypeName, defaultArrayStringTypeName;
|
||||
private SQLChecker sqlChecker;
|
||||
@ -124,9 +125,10 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
byteArrayType = types.getArrayType(types.getPrimitiveType(TypeKind.BYTE));
|
||||
sqlArrayType = elements.getTypeElement(java.sql.Array.class.getCanonicalName()).asType();
|
||||
collectionType = types.getDeclaredType(elements.getTypeElement(Collection.class.getCanonicalName()), types.getWildcardType(null, null));
|
||||
iterableType = types.getDeclaredType(elements.getTypeElement(Iterable.class.getCanonicalName()), types.getWildcardType(null, null));
|
||||
|
||||
cleanerElement = elements.getTypeElement(Cleaner.class.getCanonicalName());
|
||||
cleanerType = types.getDeclaredType(cleanerElement, types.getWildcardType(null, null));
|
||||
bindableType = elements.getTypeElement(Bindable.class.getCanonicalName()).asType();
|
||||
cleanerType = types.getDeclaredType(elements.getTypeElement(Cleaner.class.getCanonicalName()), types.getWildcardType(null, null));
|
||||
|
||||
enumType = types.getDeclaredType(elements.getTypeElement(Enum.class.getCanonicalName()), types.getWildcardType(null, null));
|
||||
|
||||
@ -344,6 +346,8 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
String calendarName = null, cleanerName = null;
|
||||
CompileTimeResultSetMapper.MaxRows maxRows = CompileTimeResultSetMapper.MaxRows.getMaxRows(sql.maxRows());
|
||||
boolean sqlExceptionThrown = false;
|
||||
boolean sqlParam = false;
|
||||
boolean sqlIterableParam = false;
|
||||
{
|
||||
// now parameters
|
||||
final List<? extends VariableElement> params = eeMethod.getParameters();
|
||||
@ -390,29 +394,40 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
continue;
|
||||
}
|
||||
unusedParams.remove(paramName);
|
||||
final String clobBlob = bindParamMatcher.group(5);
|
||||
final String clobBlobSql = bindParamMatcher.group(5);
|
||||
final String inColumnName = bindParamMatcher.group(2);
|
||||
if (inColumnName == null) {
|
||||
if(clobBlobSql == null){
|
||||
bindParamMatcher.appendReplacement(sb, "?");
|
||||
if(clobBlob == null){
|
||||
bindParams.add(bindParam);
|
||||
} else {
|
||||
// regex ensures this can only be C for clob or B for blob
|
||||
final boolean clobNotBlob = 'C' == Character.toUpperCase(clobBlob.charAt(0));
|
||||
final String blobCharset = bindParamMatcher.group(6);
|
||||
if(clobNotBlob) {
|
||||
final String upperClobBlobSql = clobBlobSql.toUpperCase();
|
||||
String blobCharset = bindParamMatcher.group(6);
|
||||
if(blobCharset != null)
|
||||
blobCharset = blobCharset.substring(0, blobCharset.indexOf(':')).trim();
|
||||
if(upperClobBlobSql.startsWith("SQL")) {
|
||||
bindParamMatcher.appendReplacement(sb, "REPLACEMEWITHUNQUOTEDQUOTEPLZ + " + paramName + " + REPLACEMEWITHUNQUOTEDQUOTEPLZ");
|
||||
final SpecialVariableElement sve = new SpecialVariableElement(bindParam, SQL, blobCharset);
|
||||
bindParams.add(sve);
|
||||
sqlParam = true;
|
||||
sqlIterableParam |= sve.iterable || sve.bindable;
|
||||
} else if(upperClobBlobSql.startsWith("CLOB") || upperClobBlobSql.startsWith("BLOB")) {
|
||||
bindParamMatcher.appendReplacement(sb, "?");
|
||||
final boolean clobNotBlob = 'C' == upperClobBlobSql.charAt(0);
|
||||
if (clobNotBlob) {
|
||||
if (blobCharset != null)
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "blob character set not valid with clob", bindParam);
|
||||
bindParams.add(new SpecialVariableElement(bindParam, SpecialVariableElement.SpecialType.CLOB));
|
||||
} else {
|
||||
bindParams.add(new SpecialVariableElement(bindParam, SpecialVariableElement.SpecialType.BLOB, blobCharset == null ? null :
|
||||
blobCharset.substring(0, blobCharset.indexOf(':')).trim()
|
||||
));
|
||||
bindParams.add(new SpecialVariableElement(bindParam, SpecialVariableElement.SpecialType.BLOB, blobCharset));
|
||||
}
|
||||
} else {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "special variable type can only be clob/blob/sql, not " + clobBlobSql, bindParam);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(clobBlob != null)
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "cannot combine in/not in and clob/blob", bindParam);
|
||||
if(clobBlobSql != null)
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "cannot combine in/not in and clob/blob/sql", bindParam);
|
||||
SpecialVariableElement inListBindParam = inListBindParams.get(paramName);
|
||||
if(inListBindParam == null) {
|
||||
inListBindParam = new SpecialVariableElement(bindParam,
|
||||
@ -502,7 +517,7 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
w.write("\t\t\tps = ");
|
||||
final boolean isGeneratedKeyLong = !parsedSQl.isSelect() && (returnType.equals("long") || returnType.equals("java.lang.Long"));
|
||||
// todo: make isGeneratedKeyLong work with cachePreparedStatements
|
||||
final boolean cachePreparedStatements = sql.cachePreparedStatement().combine(defaultCachePreparedStatements) && !bindInList && !isGeneratedKeyLong;
|
||||
final boolean cachePreparedStatements = sql.cachePreparedStatement().combine(defaultCachePreparedStatements) && !bindInList && !isGeneratedKeyLong && !sqlParam;
|
||||
if (cachePreparedStatements) {
|
||||
w.write("this.prepareStatement(");
|
||||
w.write(Integer.toString(cachedPreparedStatements));
|
||||
@ -527,15 +542,18 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
w.write(");\n");
|
||||
|
||||
// now bind parameters
|
||||
if(bindInList) {
|
||||
if(bindInList || sqlIterableParam) {
|
||||
w.write("\t\t\tint psParamCount = 0;\n");
|
||||
for (final VariableElement param : bindParams)
|
||||
setObject(w, "++psParamCount", param);
|
||||
} else {
|
||||
int count = 0;
|
||||
for (final VariableElement param : bindParams)
|
||||
for (final VariableElement param : bindParams) {
|
||||
// better place/way to do this?
|
||||
if(!(param instanceof SpecialVariableElement && ((SpecialVariableElement)param).specialType == SQL))
|
||||
setObject(w, Integer.toString(++count), param);
|
||||
}
|
||||
}
|
||||
|
||||
boolean closeRs = true;
|
||||
if (!parsedSQl.isSelect()) {
|
||||
@ -922,6 +940,16 @@ public class JdbcMapperProcessor extends AbstractProcessor {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQL: {
|
||||
if(specialParam.iterable || specialParam.bindable) {
|
||||
w.append("psParamCount = com.moparisthebest.jdbc.util.PreparedStatementUtil.recursiveBindIndex(ps, psParamCount, ").append(specialParam.name);
|
||||
if(specialParam.bindable)
|
||||
w.append(".getBindObject()"); // handle null? I think no... because then the SQL query get's "null" put in it and meh... can return noBind or an empty array...
|
||||
w.append(");");
|
||||
}
|
||||
w.append('\n');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// end special behavior
|
||||
|
@ -128,6 +128,8 @@ public class SimpleSQLChecker implements SQLChecker {
|
||||
return new ByteArrayInputStream(new byte[1]);
|
||||
case CLOB:
|
||||
return new StringReader(defaultString);
|
||||
case SQL:
|
||||
return specialParam.blobStringCharset == null ? "" : specialParam.blobStringCharset;
|
||||
}
|
||||
}
|
||||
// end special behavior
|
||||
|
@ -16,12 +16,14 @@ class SpecialVariableElement implements VariableElement {
|
||||
IN_LIST,
|
||||
CLOB,
|
||||
BLOB,
|
||||
SQL,
|
||||
}
|
||||
|
||||
final VariableElement delegate;
|
||||
final SpecialType specialType;
|
||||
final String blobStringCharset;
|
||||
final int index;
|
||||
final boolean iterable, bindable;
|
||||
|
||||
String name, componentTypeString;
|
||||
|
||||
@ -43,6 +45,8 @@ class SpecialVariableElement implements VariableElement {
|
||||
this.blobStringCharset = blobStringCharset;
|
||||
this.index = index;
|
||||
this.name = getSimpleName().toString();
|
||||
this.iterable = specialType == SpecialType.SQL && JdbcMapperProcessor.types.isAssignable(delegate.asType(), JdbcMapperProcessor.iterableType);
|
||||
this.bindable = !this.iterable && specialType == SpecialType.SQL && JdbcMapperProcessor.types.isAssignable(delegate.asType(), JdbcMapperProcessor.bindableType);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -30,6 +30,14 @@ public class ParamPatternTest {
|
||||
assertEquals("utf-16", blobCharset.substring(0, blobCharset.indexOf(':')).trim());
|
||||
blobCharset = "utf-16 : ";
|
||||
assertEquals("utf-16", blobCharset.substring(0, blobCharset.indexOf(':')).trim());
|
||||
|
||||
testMatch("{sql:sqlStatement}", s(null, null, null, null, "sql:", null, "sqlStatement"));
|
||||
testMatch("{sql: sqlStatement}", s(null, null, null, null, "sql: ", null, "sqlStatement"));
|
||||
testMatch("{SQL: sqlStatement}", s(null, null, null, null, "SQL: ", null, "sqlStatement"));
|
||||
testMatch("{Sql: sqlStatement}", s(null, null, null, null, "Sql: ", null, "sqlStatement"));
|
||||
testMatch("{Sql : sqlStatement}", s(null, null, null, null, "Sql : ", null, "sqlStatement"));
|
||||
testMatch("{sql:person:sqlStatement}", s(null, null, null, null, "sql:person:", "person:", "sqlStatement"));
|
||||
testMatch("{sql:JOIN person ON p.person_no = b.person_no:sqlStatement}", s(null, null, null, null, "sql:JOIN person ON p.person_no = b.person_no:", "JOIN person ON p.person_no = b.person_no:", "sqlStatement"));
|
||||
}
|
||||
|
||||
private static void testMatch(final String sql, final Collection<String[]> expected) {
|
||||
|
@ -1,53 +0,0 @@
|
||||
package com.moparisthebest.jdbc;
|
||||
|
||||
import com.moparisthebest.jdbc.util.InListUtil;
|
||||
|
||||
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 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
|
||||
* @param values values for in list
|
||||
* @return object
|
||||
*/
|
||||
public <T> InListObject inList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException;
|
||||
|
||||
/**
|
||||
* 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 not in list
|
||||
* @return object
|
||||
*/
|
||||
public <T> InListObject notInList(final Connection conn, final String columnName, final Collection<T> values) throws SQLException;
|
||||
|
||||
class InListObject {
|
||||
static final InListObject inEmpty = new InListObject(InListUtil.inEmpty);
|
||||
static final InListObject notInEmpty = new InListObject(InListUtil.notInEmpty);
|
||||
|
||||
private final String sql;
|
||||
|
||||
public InListObject(final String sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,9 @@
|
||||
package com.moparisthebest.jdbc;
|
||||
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
//IFJAVA8_START
|
||||
import java.util.stream.Stream;
|
||||
//IFJAVA8_END
|
||||
|
||||
import static com.moparisthebest.jdbc.InList.defaultInList;
|
||||
|
||||
public class ListQueryMapper extends QueryMapper {
|
||||
|
||||
|
@ -2,8 +2,10 @@ package com.moparisthebest.jdbc;
|
||||
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapper;
|
||||
import com.moparisthebest.jdbc.codegen.JdbcMapperFactory;
|
||||
import com.moparisthebest.jdbc.util.PreparedStatementUtil;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
import com.moparisthebest.jdbc.util.ResultSetUtil;
|
||||
import com.moparisthebest.jdbc.util.SqlBuilder;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
@ -13,15 +15,14 @@ import java.util.*;
|
||||
//IFJAVA8_START
|
||||
import java.time.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
//IFJAVA8_END
|
||||
|
||||
import static com.moparisthebest.jdbc.InList.defaultInList;
|
||||
import static com.moparisthebest.jdbc.TryClose.tryClose;
|
||||
|
||||
public class QueryMapper implements JdbcMapper {
|
||||
|
||||
public static final Object noBind = new Object();
|
||||
public static final Object noBind = PreparedStatementUtil.noBind;
|
||||
public static final ResultSetMapper defaultRsm = new ResultSetMapper();
|
||||
|
||||
protected static final int[] ORACLE_SINGLE_COLUMN_INDEX = new int[]{1};
|
||||
@ -51,46 +52,8 @@ public class QueryMapper implements JdbcMapper {
|
||||
return conn.prepareStatement(sql);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
IFJAVA6_END*/
|
||||
|
||||
protected static final InList defaultInList = getDefaultInList();
|
||||
|
||||
private static InList getDefaultInList() {
|
||||
try {
|
||||
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"));
|
||||
return (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")) {
|
||||
return OptimalInList.instance();
|
||||
} else {
|
||||
switch (JdbcMapper.DatabaseType.valueOf(type)) {
|
||||
case DEFAULT:
|
||||
case BIND:
|
||||
return BindInList.instance();
|
||||
case ANY:
|
||||
return ArrayInList.instance();
|
||||
case ORACLE:
|
||||
return OracleArrayInList.instance();
|
||||
case UNNEST:
|
||||
return UnNestArrayInList.instance();
|
||||
default:
|
||||
throw new RuntimeException("Invalid queryMapper.databaseType: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// NEVER ignore
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected final ResultSetMapper cm;
|
||||
protected final Connection conn;
|
||||
protected final boolean closeConn;
|
||||
@ -167,53 +130,16 @@ public class QueryMapper implements JdbcMapper {
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringWrapper {
|
||||
public final String s;
|
||||
|
||||
private StringWrapper(String s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return s;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof StringWrapper)) return false;
|
||||
StringWrapper that = (StringWrapper) o;
|
||||
return !(s != null ? !s.equals(that.s) : that.s != null);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return s != null ? s.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClobString extends StringWrapper {
|
||||
private ClobString(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BlobString extends StringWrapper {
|
||||
private final Charset charset;
|
||||
private BlobString(final String s, final Charset charset) {
|
||||
super(s);
|
||||
this.charset = charset;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object wrapClob(String s) {
|
||||
return new ClobString(s);
|
||||
return PreparedStatementUtil.wrapClob(s);
|
||||
}
|
||||
|
||||
public static Object wrapBlob(String s) {
|
||||
return new BlobString(s, UTF_8);
|
||||
return PreparedStatementUtil.wrapBlob(s);
|
||||
}
|
||||
|
||||
public static Object wrapBlob(final String s, final Charset charset) {
|
||||
return new BlobString(s, charset == null ? UTF_8 : charset);
|
||||
return PreparedStatementUtil.wrapBlob(s, charset);
|
||||
}
|
||||
|
||||
// start in list specific code
|
||||
@ -238,6 +164,10 @@ public class QueryMapper implements JdbcMapper {
|
||||
recursiveReplace(sb, (Object[]) o);
|
||||
} else if (o instanceof Collection) {
|
||||
recursiveReplace(sb, ((Collection) o).toArray());
|
||||
} else if(o instanceof Iterable) {
|
||||
for(final Object o2 : (Iterable) o) {
|
||||
recursiveReplace(sb, o2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -262,142 +192,25 @@ public class QueryMapper implements JdbcMapper {
|
||||
// end in list specific code
|
||||
|
||||
public static void setObject(final PreparedStatement ps, final int index, final Object o) throws SQLException {
|
||||
// we are going to put most common ones up top so it should execute faster normally
|
||||
if (o == null || o instanceof String || o instanceof Number)
|
||||
ps.setObject(index, o);
|
||||
// java.util.Date support, put it in a Timestamp
|
||||
else if (o instanceof java.util.Date)
|
||||
ps.setObject(index, o.getClass().equals(java.util.Date.class) ? new java.sql.Timestamp(((java.util.Date)o).getTime()) : o);
|
||||
//IFJAVA8_START// todo: other java.time types
|
||||
else if (o instanceof Instant)
|
||||
ps.setObject(index, java.sql.Timestamp.from((Instant)o));
|
||||
else if (o instanceof LocalDateTime)
|
||||
ps.setObject(index, java.sql.Timestamp.valueOf((LocalDateTime)o));
|
||||
else if (o instanceof LocalDate)
|
||||
ps.setObject(index, java.sql.Date.valueOf((LocalDate)o));
|
||||
else if (o instanceof LocalTime)
|
||||
ps.setObject(index, java.sql.Time.valueOf((LocalTime)o));
|
||||
else if (o instanceof ZonedDateTime)
|
||||
ps.setObject(index, java.sql.Timestamp.from(((ZonedDateTime)o).toInstant()));
|
||||
else if (o instanceof OffsetDateTime)
|
||||
ps.setObject(index, java.sql.Timestamp.from(((OffsetDateTime)o).toInstant()));
|
||||
else if (o instanceof OffsetTime)
|
||||
ps.setObject(index, java.sql.Time.valueOf(((OffsetTime)o).toLocalTime())); // todo: no timezone?
|
||||
|
||||
//IFJAVA8_END
|
||||
// CLOB support
|
||||
else if (o instanceof Reader)
|
||||
ps.setClob(index, (Reader) o);
|
||||
else if (o instanceof ClobString)
|
||||
ps.setClob(index, ((ClobString) o).s == null ? null : new StringReader(((ClobString) o).s));
|
||||
else if (o instanceof java.sql.Clob)
|
||||
ps.setClob(index, (java.sql.Clob) o);
|
||||
// BLOB support
|
||||
else if (o instanceof byte[])
|
||||
ps.setBlob(index, new ByteArrayInputStream((byte[]) o));
|
||||
else if (o instanceof InputStream)
|
||||
ps.setBlob(index, (InputStream) o);
|
||||
else if (o instanceof File)
|
||||
try {
|
||||
ps.setBlob(index, new FileInputStream((File) o)); // todo: does this close this or leak a file descriptor?
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new SQLException("File to Blob FileNotFoundException", e);
|
||||
}
|
||||
else if (o instanceof BlobString)
|
||||
ps.setBlob(index, ((BlobString) o).s == null ? null : new ByteArrayInputStream(((BlobString) o).s.getBytes(((BlobString) o).charset)));
|
||||
else if (o instanceof java.sql.Blob)
|
||||
ps.setBlob(index, (java.sql.Blob) o);
|
||||
else if (o instanceof ArrayInList.ArrayListObject)
|
||||
ps.setArray(index, ((ArrayInList.ArrayListObject) o).getArray());
|
||||
else if (o instanceof java.sql.Array)
|
||||
ps.setArray(index, (java.sql.Array) o);
|
||||
else if (o instanceof Enum)
|
||||
ps.setObject(index, ((Enum)o).name());
|
||||
else
|
||||
ps.setObject(index, o); // probably won't get here ever, but just in case...
|
||||
/*
|
||||
switch(ps.getParameterMetaData().getParameterType(index)){ // 'java.sql.SQLException: Unsupported feature', fully JDBC 3.0 compliant my ass, freaking oracle...
|
||||
case Types.CLOB:
|
||||
if(o instanceof String)
|
||||
ps.setObject(index, o);
|
||||
else if (o instanceof Reader)
|
||||
ps.setClob(index, (Reader) o);
|
||||
else if (o instanceof Clob)
|
||||
ps.setClob(index, (Clob) o);
|
||||
return;
|
||||
case Types.BLOB:
|
||||
if (o instanceof byte[])
|
||||
ps.setBlob(index, new ByteArrayInputStream((byte[])o));
|
||||
else if (o instanceof InputStream)
|
||||
ps.setBlob(index, (InputStream) o);
|
||||
else if (o instanceof File)
|
||||
try {
|
||||
ps.setBlob(index, new FileInputStream((File) o));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new SQLException("File to Blob FileNotFoundException", e);
|
||||
}
|
||||
else if (o instanceof Blob)
|
||||
ps.setBlob(index, (Blob) o);
|
||||
else if(o instanceof String)
|
||||
try{
|
||||
ps.setBlob(index, new ByteArrayInputStream(((String) o).getBytes("UTF-8")));
|
||||
}catch(UnsupportedEncodingException e){
|
||||
throw new SQLException("String to Blob UnsupportedEncodingException", e);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
ps.setObject(index, o);
|
||||
}
|
||||
*/
|
||||
PreparedStatementUtil.setObject(ps, index, o);
|
||||
}
|
||||
|
||||
public static int recursiveBind(final PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
return recursiveBind(ps, 0, bindObjects);
|
||||
}
|
||||
|
||||
private static int recursiveBind(final PreparedStatement ps, int index, final Object... bindObjects) throws SQLException {
|
||||
if (bindObjects != null && bindObjects.length > 0) {
|
||||
for (Object o : bindObjects) {
|
||||
if (o != null) {
|
||||
if (o == InList.InListObject.inEmpty || o == InList.InListObject.notInEmpty || o == noBind) {
|
||||
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) {
|
||||
index = recursiveBind(ps, index, ((Collection) o).toArray());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//System.out.printf("index: '%d' bound to '%s'\n", index+1, o);
|
||||
setObject(ps, ++index, o);
|
||||
//ps.setObject(++index, o);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
return PreparedStatementUtil.recursiveBind(ps, bindObjects);
|
||||
}
|
||||
|
||||
public static PreparedStatement bindStatement(final PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
recursiveBind(ps, bindObjects);
|
||||
return ps;
|
||||
}
|
||||
|
||||
protected static PreparedStatement bind(final PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
return bindStatement(ps, bindObjects);
|
||||
return PreparedStatementUtil.bind(ps, bindObjects);
|
||||
}
|
||||
|
||||
protected static ResultSet bindExecute(final PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
return bind(ps, bindObjects).executeQuery();
|
||||
return PreparedStatementUtil.bind(ps, bindObjects).executeQuery();
|
||||
}
|
||||
|
||||
// these update the database
|
||||
|
||||
public int executeUpdate(PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
return bind(ps, bindObjects).executeUpdate();
|
||||
return PreparedStatementUtil.bind(ps, bindObjects).executeUpdate();
|
||||
}
|
||||
|
||||
public boolean executeUpdateSuccess(PreparedStatement ps, final Object... bindObjects) throws SQLException {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package com.moparisthebest.jdbc.codegen;
|
||||
|
||||
import com.moparisthebest.jdbc.dto.*;
|
||||
import com.moparisthebest.jdbc.util.CaseInsensitiveHashMap;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
import com.moparisthebest.jdbc.util.*;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -264,4 +264,23 @@ public interface QmDao extends JdbcMapper {
|
||||
|
||||
@SQL("INSERT INTO a_thaoeu_table (a_thaoeu_table_val) VALUES ({value})")
|
||||
long insertGetGeneratedKey(long value) throws SQLException;
|
||||
|
||||
@SQL("SELECT person_no FROM person WHERE {sql:sql}")
|
||||
List<Long> selectRandomSql(String sql) throws SQLException;
|
||||
|
||||
@SQL("SELECT person_no FROM person WHERE {sql:sql}")
|
||||
List<Long> selectRandomSqlBuilder(SqlBuilder sql) throws SQLException;
|
||||
|
||||
@SQL("SELECT person_no FROM person WHERE person_no = {personNo1} {sql:sql} OR first_name = {firstName}")
|
||||
List<Long> selectRandomSql(long personNo1, String sql, String firstName) throws SQLException;
|
||||
|
||||
@SQL("SELECT person_no FROM person WHERE person_no = {personNo1} {sql:sql} OR first_name = {firstName}")
|
||||
List<Long> selectRandomSqlBuilder(long personNo1, Bindable sql, String firstName) throws SQLException;
|
||||
|
||||
// these we just check if they generated
|
||||
@SQL("INSERT {sql:sql}")
|
||||
void insertRandomSqlCollection(Collection<Long> sql) throws SQLException;
|
||||
|
||||
@SQL("INSERT {sql:sql}")
|
||||
void insertRandomSqlIterable(Iterable<Long> sql) throws SQLException;
|
||||
}
|
||||
|
@ -2,8 +2,7 @@ package com.moparisthebest.jdbc.codegen;
|
||||
|
||||
import com.moparisthebest.jdbc.*;
|
||||
import com.moparisthebest.jdbc.dto.*;
|
||||
import com.moparisthebest.jdbc.util.CaseInsensitiveHashMap;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
import com.moparisthebest.jdbc.util.*;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
@ -413,4 +412,34 @@ public class QueryMapperQmDao implements QmDao {
|
||||
public long insertGetGeneratedKey(long value) throws SQLException {
|
||||
return qm.insertGetGeneratedKey("INSERT INTO a_thaoeu_table (a_thaoeu_table_val) VALUES (?)", value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSql(final String sql) throws SQLException {
|
||||
return qm.toList("SELECT person_no FROM person WHERE " + sql, Long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSqlBuilder(final SqlBuilder sql) throws SQLException {
|
||||
return qm.toList("SELECT person_no FROM person WHERE " + sql, Long.class, sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSql(final long personNo1, final String sql, final String firstName) throws SQLException {
|
||||
return qm.toList("SELECT person_no FROM person WHERE person_no = ? " + sql + " OR first_name = ?", Long.class, personNo1, firstName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSqlBuilder(final long personNo1, final Bindable sql, final String firstName) throws SQLException {
|
||||
return qm.toList("SELECT person_no FROM person WHERE person_no = ? " + sql + " OR first_name = ?", Long.class, personNo1, sql, firstName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertRandomSqlCollection(final Collection<Long> sql) throws SQLException {
|
||||
qm.executeUpdate("INSERT " + sql, sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertRandomSqlIterable(final Iterable<Long> sql) throws SQLException {
|
||||
qm.executeUpdate("INSERT " + sql, sql);
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,7 @@ package com.moparisthebest.jdbc.codegen;
|
||||
import com.moparisthebest.jdbc.ResultSetMapper;
|
||||
import com.moparisthebest.jdbc.TypeReference;
|
||||
import com.moparisthebest.jdbc.dto.*;
|
||||
import com.moparisthebest.jdbc.util.CaseInsensitiveHashMap;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
import com.moparisthebest.jdbc.util.*;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
@ -358,4 +357,24 @@ public class QueryMapperTypeQmDao extends QueryMapperQmDao {
|
||||
public List<FieldPerson> getFieldPeopleNotIn(final List<Long> personNos) throws SQLException {
|
||||
return qm.toType("SELECT * from person WHERE " + inListReplace + " ORDER BY person_no", new TypeReference<List<FieldPerson>>() {}, qm.notInList("person_no", personNos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSql(final String sql) throws SQLException {
|
||||
return qm.toType("SELECT person_no FROM person WHERE " + sql, new TypeReference<List<Long>>() {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSqlBuilder(final SqlBuilder sql) throws SQLException {
|
||||
return qm.toType("SELECT person_no FROM person WHERE " + sql, new TypeReference<List<Long>>() {}, sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSql(final long personNo1, final String sql, final String firstName) throws SQLException {
|
||||
return qm.toType("SELECT person_no FROM person WHERE person_no = ? " + sql + " OR first_name = ?", new TypeReference<List<Long>>() {}, personNo1, firstName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> selectRandomSqlBuilder(final long personNo1, final Bindable sql, final String firstName) throws SQLException {
|
||||
return qm.toType("SELECT person_no FROM person WHERE person_no = ? " + sql + " OR first_name = ?", new TypeReference<List<Long>>() {}, personNo1, sql, firstName);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ import com.moparisthebest.jdbc.codegen.QmDao;
|
||||
import com.moparisthebest.jdbc.codegen.QueryMapperQmDao;
|
||||
import com.moparisthebest.jdbc.codegen.QueryMapperTypeQmDao;
|
||||
import com.moparisthebest.jdbc.dto.*;
|
||||
import com.moparisthebest.jdbc.util.Bindable;
|
||||
import com.moparisthebest.jdbc.util.ResultSetIterable;
|
||||
import com.moparisthebest.jdbc.util.SqlBuilder;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
@ -756,4 +758,24 @@ public class QueryMapperTest {
|
||||
final List<FieldPerson> fromDb = qm.getFieldPeopleNotIn(Arrays.asList(bosses[0].getPersonNo(), bosses[1].getPersonNo(), bosses[2].getPersonNo()));
|
||||
assertArrayEquals(people, fromDb.toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectRandomSql() throws Throwable {
|
||||
final List<Long> arr = Arrays.asList(1L, 2L, 3L);
|
||||
assertEquals(arr, qm.selectRandomSql("person_no in (1,2,3)"));
|
||||
assertEquals(arr, qm.selectRandomSql(1L, " OR person_no in (2,3)", "NoNameMatch"));
|
||||
assertEquals(Collections.singletonList(2L), qm.selectRandomSql(2L, "", "NoNameMatch"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectRandomSqlBuilder() throws Throwable {
|
||||
final List<Long> arr = Arrays.asList(1L, 2L, 3L);
|
||||
assertEquals(arr, qm.selectRandomSqlBuilder(SqlBuilder.of(qm.getConnection()).appendInList("person_no", arr)));
|
||||
assertEquals(arr, qm.selectRandomSqlBuilder(SqlBuilder.of(qm.getConnection()).append("person_no = ? OR ", 1L).appendInList("person_no", Arrays.asList(2L, 3L))));
|
||||
assertEquals(arr, qm.selectRandomSqlBuilder(SqlBuilder.of(qm.getConnection()).append("person_no = 1 OR ").appendInList("person_no", Arrays.asList(2L, 3L))));
|
||||
assertEquals(arr, qm.selectRandomSqlBuilder(1L, SqlBuilder.of(qm.getConnection()).append(" OR person_no in (2,3)"), "NoNameMatch"));
|
||||
assertEquals(Collections.singletonList(2L), qm.selectRandomSqlBuilder(2L, SqlBuilder.of(qm.getConnection()), "NoNameMatch"));
|
||||
assertEquals(Collections.singletonList(3L), qm.selectRandomSqlBuilder(3L, Bindable.empty, "NoNameMatch"));
|
||||
assertEquals(arr, qm.selectRandomSqlBuilder(2L, SqlBuilder.of(qm.getConnection()).append("OR person_no = ? OR ", 1L).appendInList("person_no", Collections.singletonList(3L)), "NoNameMatch"));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user