[jOOQ/jOOQ#10432] Add Settings.cachePreparedStatementsInLoader to keeping open PreparedStatements in Loader API
This commit is contained in:
parent
06f15008cc
commit
9e3a1dc5ec
@ -158,6 +158,8 @@ public class Settings
|
||||
protected Boolean reflectionCaching = true;
|
||||
@XmlElement(defaultValue = "true")
|
||||
protected Boolean cacheRecordMappers = true;
|
||||
@XmlElement(defaultValue = "true")
|
||||
protected Boolean cachePreparedStatementInLoader = true;
|
||||
@XmlElement(defaultValue = "THROW_ALL")
|
||||
@XmlSchemaType(name = "string")
|
||||
protected ThrowExceptions throwExceptions = ThrowExceptions.THROW_ALL;
|
||||
@ -1363,6 +1365,30 @@ public class Settings
|
||||
this.cacheRecordMappers = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether JDBC {@link java.sql.PreparedStatement} instances should be cached in loader API.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public Boolean isCachePreparedStatementInLoader() {
|
||||
return cachePreparedStatementInLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the cachePreparedStatementInLoader property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link Boolean }
|
||||
*
|
||||
*/
|
||||
public void setCachePreparedStatementInLoader(Boolean value) {
|
||||
this.cachePreparedStatementInLoader = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A strategy defining how exceptions from the database / JDBC driver should be propagated
|
||||
*
|
||||
@ -2563,6 +2589,11 @@ public class Settings
|
||||
return this;
|
||||
}
|
||||
|
||||
public Settings withCachePreparedStatementInLoader(Boolean value) {
|
||||
setCachePreparedStatementInLoader(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A strategy defining how exceptions from the database / JDBC driver should be propagated
|
||||
*
|
||||
@ -2943,6 +2974,7 @@ public class Settings
|
||||
builder.append("updatablePrimaryKeys", updatablePrimaryKeys);
|
||||
builder.append("reflectionCaching", reflectionCaching);
|
||||
builder.append("cacheRecordMappers", cacheRecordMappers);
|
||||
builder.append("cachePreparedStatementInLoader", cachePreparedStatementInLoader);
|
||||
builder.append("throwExceptions", throwExceptions);
|
||||
builder.append("fetchWarnings", fetchWarnings);
|
||||
builder.append("fetchServerOutputSize", fetchServerOutputSize);
|
||||
@ -3446,6 +3478,15 @@ public class Settings
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (cachePreparedStatementInLoader == null) {
|
||||
if (other.cachePreparedStatementInLoader!= null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!cachePreparedStatementInLoader.equals(other.cachePreparedStatementInLoader)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (throwExceptions == null) {
|
||||
if (other.throwExceptions!= null) {
|
||||
return false;
|
||||
@ -3871,6 +3912,7 @@ public class Settings
|
||||
result = ((prime*result)+((updatablePrimaryKeys == null)? 0 :updatablePrimaryKeys.hashCode()));
|
||||
result = ((prime*result)+((reflectionCaching == null)? 0 :reflectionCaching.hashCode()));
|
||||
result = ((prime*result)+((cacheRecordMappers == null)? 0 :cacheRecordMappers.hashCode()));
|
||||
result = ((prime*result)+((cachePreparedStatementInLoader == null)? 0 :cachePreparedStatementInLoader.hashCode()));
|
||||
result = ((prime*result)+((throwExceptions == null)? 0 :throwExceptions.hashCode()));
|
||||
result = ((prime*result)+((fetchWarnings == null)? 0 :fetchWarnings.hashCode()));
|
||||
result = ((prime*result)+((fetchServerOutputSize == null)? 0 :fetchServerOutputSize.hashCode()));
|
||||
|
||||
@ -84,8 +84,6 @@ final class JSONReader {
|
||||
@SuppressWarnings("rawtypes")
|
||||
final Result<Record> read(final Reader reader) {
|
||||
try {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Object root = new JSONParser().parse(reader, new ContainerFactory() {
|
||||
@Override
|
||||
public Map createObjectContainer() {
|
||||
|
||||
@ -37,11 +37,14 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
import static org.jooq.impl.Tools.EMPTY_FIELD;
|
||||
import static org.jooq.impl.Tools.combine;
|
||||
import static org.jooq.tools.jdbc.JDBCUtils.safeClose;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -50,13 +53,16 @@ import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -64,7 +70,9 @@ import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.jooq.BatchBindStep;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.ConnectionRunnable;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.InsertQuery;
|
||||
import org.jooq.Loader;
|
||||
@ -91,6 +99,7 @@ import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.StringUtils;
|
||||
import org.jooq.tools.csv.CSVParser;
|
||||
import org.jooq.tools.csv.CSVReader;
|
||||
import org.jooq.tools.jdbc.DefaultPreparedStatement;
|
||||
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
@ -141,7 +150,6 @@ final class LoaderImpl<R extends Record> implements
|
||||
|
||||
// Configuration data
|
||||
// ------------------
|
||||
private final DSLContext create;
|
||||
private final Configuration configuration;
|
||||
private final Table<R> table;
|
||||
private int onDuplicate = ON_DUPLICATE_KEY_ERROR;
|
||||
@ -180,7 +188,6 @@ final class LoaderImpl<R extends Record> implements
|
||||
private final List<LoaderError> errors;
|
||||
|
||||
LoaderImpl(Configuration configuration, Table<R> table) {
|
||||
this.create = DSL.using(configuration);
|
||||
this.configuration = configuration;
|
||||
this.table = table;
|
||||
this.errors = new ArrayList<>();
|
||||
@ -669,12 +676,12 @@ final class LoaderImpl<R extends Record> implements
|
||||
throw new LoaderConfigurationException("Cannot apply bulk loading with onDuplicateKey flags. Turn off either flag.");
|
||||
}
|
||||
|
||||
private final void executeJSON() throws IOException {
|
||||
private final void executeJSON() {
|
||||
Reader reader = null;
|
||||
|
||||
try {
|
||||
reader = input.reader();
|
||||
Result<Record> r = new JSONReader(create).read(reader);
|
||||
Result<Record> r = new JSONReader(configuration.dsl()).read(reader);
|
||||
source = r.fields();
|
||||
|
||||
// The current json format is not designed for streaming. Thats why
|
||||
@ -682,19 +689,12 @@ final class LoaderImpl<R extends Record> implements
|
||||
List<Object[]> allRecords = Arrays.asList(r.intoArrays());
|
||||
executeSQL(allRecords.iterator());
|
||||
}
|
||||
|
||||
// SQLExceptions originating from rollbacks or commits are always fatal
|
||||
// They are propagated, and not swallowed
|
||||
catch (SQLException e) {
|
||||
throw Tools.translate(null, e);
|
||||
}
|
||||
finally {
|
||||
if (reader != null)
|
||||
reader.close();
|
||||
safeClose(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private final void executeCSV() throws IOException {
|
||||
private final void executeCSV() {
|
||||
CSVReader reader = null;
|
||||
|
||||
try {
|
||||
@ -708,31 +708,87 @@ final class LoaderImpl<R extends Record> implements
|
||||
|
||||
executeSQL(reader);
|
||||
}
|
||||
|
||||
// SQLExceptions originating from rollbacks or commits are always fatal
|
||||
// They are propagated, and not swallowed
|
||||
catch (SQLException e) {
|
||||
throw Tools.translate(null, e);
|
||||
}
|
||||
finally {
|
||||
if (reader != null)
|
||||
reader.close();
|
||||
safeClose(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private final void executeRows() {
|
||||
try {
|
||||
executeSQL(arrays);
|
||||
executeSQL(arrays);
|
||||
}
|
||||
|
||||
private static final class CachedPSListener extends DefaultExecuteListener {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 1374352882405299310L;
|
||||
|
||||
final Map<String, CachedPS> map = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void prepareStart(ExecuteContext ctx) {
|
||||
CachedPS ps = map.get(ctx.sql());
|
||||
|
||||
if (ps != null)
|
||||
ctx.statement(ps);
|
||||
}
|
||||
|
||||
// SQLExceptions originating from rollbacks or commits are always fatal
|
||||
// They are propagated, and not swallowed
|
||||
catch (SQLException e) {
|
||||
throw Tools.translate(null, e);
|
||||
@Override
|
||||
public void prepareEnd(ExecuteContext ctx) {
|
||||
if (!(ctx.statement() instanceof CachedPS)) {
|
||||
CachedPS ps = new CachedPS(ctx.statement());
|
||||
map.put(ctx.sql(), ps);
|
||||
ctx.statement(ps);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for (CachedPS ps : map.values())
|
||||
safeClose(ps.getDelegate());
|
||||
}
|
||||
}
|
||||
|
||||
private final void executeSQL(Iterator<? extends Object[]> iterator) throws SQLException {
|
||||
private static class CachedPS extends DefaultPreparedStatement {
|
||||
CachedPS(PreparedStatement delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {}
|
||||
}
|
||||
|
||||
private final void executeSQL(Iterator<? extends Object[]> iterator) {
|
||||
configuration.dsl().connection(new ConnectionRunnable() {
|
||||
@Override
|
||||
public void run(Connection connection) throws Exception {
|
||||
Configuration c = configuration.derive(new DefaultConnectionProvider(connection));
|
||||
|
||||
if (FALSE.equals(c.settings().isCachePreparedStatementInLoader())) {
|
||||
executeSQL(iterator, c.dsl());
|
||||
}
|
||||
|
||||
else {
|
||||
CachedPSListener cache = new CachedPSListener();
|
||||
|
||||
try {
|
||||
executeSQL(iterator, c
|
||||
.derive(combine(new DefaultExecuteListenerProvider(cache), c.executeListenerProviders()))
|
||||
.dsl()
|
||||
);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
cache.close();
|
||||
}
|
||||
catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final void executeSQL(Iterator<? extends Object[]> iterator, DSLContext ctx) throws SQLException {
|
||||
Object[] row = null;
|
||||
BatchBindStep bind = null;
|
||||
InsertQuery<R> insert = null;
|
||||
@ -769,7 +825,7 @@ final class LoaderImpl<R extends Record> implements
|
||||
buffered++;
|
||||
|
||||
if (insert == null)
|
||||
insert = create.insertQuery(table);
|
||||
insert = ctx.insertQuery(table);
|
||||
|
||||
if (newRecord) {
|
||||
newRecord = false;
|
||||
@ -811,7 +867,7 @@ final class LoaderImpl<R extends Record> implements
|
||||
|
||||
if (batch != BATCH_NONE) {
|
||||
if (bind == null)
|
||||
bind = create.batch(insert);
|
||||
bind = ctx.batch(insert);
|
||||
|
||||
bind.bind(insert.getBindValues().toArray());
|
||||
insert = null;
|
||||
@ -831,7 +887,7 @@ final class LoaderImpl<R extends Record> implements
|
||||
// [#10358] The MySQL dialect category doesn't return rowcounts
|
||||
// in INSERT .. ON DUPLICATE KEY UPDATE statements, but
|
||||
// 1 = INSERT, 2 = UPDATE, instead
|
||||
if (onDuplicate == ON_DUPLICATE_KEY_UPDATE && NO_SUPPORT_ROWCOUNT_ON_DUPLICATE.contains(create.dialect()))
|
||||
if (onDuplicate == ON_DUPLICATE_KEY_UPDATE && NO_SUPPORT_ROWCOUNT_ON_DUPLICATE.contains(ctx.dialect()))
|
||||
totalRowCounts = buffered;
|
||||
else
|
||||
for (int rowCount : rowcounts)
|
||||
|
||||
@ -1247,7 +1247,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
? new Field[] { DSL.field("*") }
|
||||
: Tools.aliasedFields(originalFields),
|
||||
|
||||
null
|
||||
(Field<?>) null
|
||||
);
|
||||
|
||||
alternativeFields[alternativeFields.length - 1] =
|
||||
|
||||
@ -2723,12 +2723,16 @@ final class Tools {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <T> T[] combine(T[] array, T value) {
|
||||
static final <T> T[] combine(T value, T[] array) {
|
||||
T[] result = (T[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), array.length + 1);
|
||||
result[0] = value;
|
||||
System.arraycopy(array, 0, result, 1, array.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
System.arraycopy(array, 0, result, 0, array.length);
|
||||
static final <T> T[] combine(T[] array, T value) {
|
||||
T[] result = Arrays.copyOf(array, array.length + 1);;
|
||||
result[array.length] = value;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -341,6 +341,10 @@ UpdatableRecord.store() and UpdatableRecord.update().]]></jxb:javadoc></jxb:prop
|
||||
<element name="cacheRecordMappers" type="boolean" minOccurs="0" maxOccurs="1" default="true">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether record mappers should be cached in the configuration.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="cachePreparedStatementInLoader" type="boolean" minOccurs="0" maxOccurs="1" default="true">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether JDBC {@link java.sql.PreparedStatement} instances should be cached in loader API.]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
</element>
|
||||
|
||||
<element name="throwExceptions" type="jooq-runtime:ThrowExceptions" minOccurs="0" maxOccurs="1" default="THROW_ALL">
|
||||
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A strategy defining how exceptions from the database / JDBC driver should be propagated]]></jxb:javadoc></jxb:property></appinfo></annotation>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user