add batch methods to SqlUpdate

This commit is contained in:
Jörg Prante 2022-08-01 11:23:14 +02:00
parent a9fb8eaa2f
commit 1c1a1ed55a
4 changed files with 114 additions and 11 deletions

View file

@ -1,6 +1,6 @@
group = org.xbib group = org.xbib
name = database name = database
version = 0.0.3 version = 0.0.4
org.gradle.warning.mode = ALL org.gradle.warning.mode = ALL

View file

@ -269,7 +269,7 @@ public class SqlInsertImpl implements SqlInsert {
@Override @Override
public void insertBatch() { public void insertBatch() {
int[] result = updateBatch(); int[] result = insertBatchInternal();
for (int r : result) { for (int r : result) {
// Tolerate SUCCESS_NO_INFO for older versions of Oracle // Tolerate SUCCESS_NO_INFO for older versions of Oracle
if (r != 1 && r != Statement.SUCCESS_NO_INFO) { if (r != 1 && r != Statement.SUCCESS_NO_INFO) {
@ -280,7 +280,7 @@ public class SqlInsertImpl implements SqlInsert {
@Override @Override
public int[] insertBatchUnchecked() { public int[] insertBatchUnchecked() {
return updateBatch(); return insertBatchInternal();
} }
@Override @Override
@ -414,7 +414,7 @@ public class SqlInsertImpl implements SqlInsert {
return pkArgName != null || pkSeqName != null || pkLong != null; return pkArgName != null || pkSeqName != null || pkLong != null;
} }
private int[] updateBatch() { private int[] insertBatchInternal() {
batch(); batch();
if (batched == null || batched.size() == 0) { if (batched == null || batched.size() == 0) {
@ -682,7 +682,7 @@ public class SqlInsertImpl implements SqlInsert {
return b == null ? null : b ? "Y" : "N"; return b == null ? null : b ? "Y" : "N";
} }
private class Batch { private static class Batch {
private final List<Object> parameterList; // !null ==> traditional ? args private final List<Object> parameterList; // !null ==> traditional ? args
private final Map<String, Object> parameterMap; // !null ==> named :abc args private final Map<String, Object> parameterMap; // !null ==> named :abc args

View file

@ -77,6 +77,8 @@ public interface SqlUpdate {
SqlUpdate apply(Apply apply); SqlUpdate apply(Apply apply);
SqlUpdate batch();
/** /**
* Execute the SQL update and return the number of rows was affected. * Execute the SQL update and return the number of rows was affected.
*/ */
@ -90,6 +92,10 @@ public interface SqlUpdate {
*/ */
void update(int expectedRowsUpdated); void update(int expectedRowsUpdated);
void updateBatch();
int[] updateBatchUnchecked();
interface Apply { interface Apply {
void apply(SqlUpdate update); void apply(SqlUpdate update);
} }

View file

@ -1,5 +1,6 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import org.xbib.jdbc.query.util.DebugSql; import org.xbib.jdbc.query.util.DebugSql;
import org.xbib.jdbc.query.util.InternalStringReader; import org.xbib.jdbc.query.util.InternalStringReader;
@ -14,6 +15,7 @@ import java.sql.PreparedStatement;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -35,6 +37,8 @@ public class SqlUpdateImpl implements SqlUpdate {
private final Options options; private final Options options;
private List<Batch> batched;
private List<Object> parameterList; private List<Object> parameterList;
private Map<String, Object> parameterMap; private Map<String, Object> parameterMap;
@ -247,6 +251,20 @@ public class SqlUpdateImpl implements SqlUpdate {
return this; return this;
} }
@Override
public SqlUpdate batch() {
if ((parameterList != null && !parameterList.isEmpty())
|| (parameterMap != null && !parameterMap.isEmpty())) {
if (batched == null) {
batched = new ArrayList<>();
}
batched.add(new Batch(parameterList, parameterMap));
parameterList = new ArrayList<>();
parameterMap = new HashMap<>();
}
return this;
}
@Override @Override
public int update() { public int update() {
return updateInternal(0); return updateInternal(0);
@ -257,13 +275,85 @@ public class SqlUpdateImpl implements SqlUpdate {
updateInternal(expectedNumAffectedRows); updateInternal(expectedNumAffectedRows);
} }
@Override
public void updateBatch() {
int[] result = updateBatchInternal();
for (int r : result) {
// Tolerate SUCCESS_NO_INFO for older versions of Oracle
if (r != 1 && r != Statement.SUCCESS_NO_INFO) {
throw new DatabaseException("Batch did not return the expected result: " + Arrays.toString(result));
}
}
}
@Override
public int[] updateBatchUnchecked() {
return updateBatchInternal();
}
private int[] updateBatchInternal() {
batch();
if (batched == null || batched.size() == 0) {
throw new DatabaseException("Batch insert requires parameters");
}
PreparedStatement ps = null;
Metric metric = new Metric(log.isLoggable(Level.FINE));
String executeSql = sql;
Object[] firstRowParameters = null;
List<Object[]> parameters = new ArrayList<>();
boolean isSuccess = false;
String errorCode = null;
Exception logEx = null;
try {
for (Batch batch : batched) {
MixedParameterSql mpSql = new MixedParameterSql(sql, batch.parameterList, batch.parameterMap);
if (firstRowParameters == null) {
executeSql = mpSql.getSqlToExecute();
firstRowParameters = mpSql.getArgs();
} else {
if (!executeSql.equals(mpSql.getSqlToExecute())) {
throw new DatabaseException("All rows in a batch must use parameters in the same way. \nSQL1: "
+ executeSql + "\nSQL2: " + mpSql.getSqlToExecute());
}
}
parameters.add(mpSql.getArgs());
}
if (connection != null) {
ps = connection.prepareStatement(executeSql);
for (Object[] params : parameters) {
adaptor.addParameters(ps, params);
ps.addBatch();
}
metric.checkpoint("prep");
int[] numAffectedRows = ps.executeBatch();
metric.checkpoint("execBatch", parameters.size());
isSuccess = true;
return numAffectedRows;
} else {
return new int[]{-1};
}
} catch (WrongNumberOfRowsException e) {
throw e;
} catch (Exception e) {
errorCode = options.generateErrorCode();
logEx = e;
throw DatabaseException.wrap(DebugSql.exceptionMessage(executeSql, firstRowParameters, errorCode, options), e);
} finally {
adaptor.closeQuietly(ps, log);
metric.done("close");
if (isSuccess) {
DebugSql.logSuccess("Insert", log, metric, executeSql, firstRowParameters, options);
} else {
DebugSql.logError("Insert", log, metric, errorCode, executeSql, firstRowParameters, options, logEx);
}
}
}
private int updateInternal(int expectedNumAffectedRows) { private int updateInternal(int expectedNumAffectedRows) {
PreparedStatement ps = null; PreparedStatement ps = null;
Metric metric = new Metric(log.isLoggable(Level.FINE)); Metric metric = new Metric(log.isLoggable(Level.FINE));
String executeSql = sql; String executeSql = sql;
Object[] parameters = null; Object[] parameters = null;
boolean isSuccess = false; boolean isSuccess = false;
String errorCode = null; String errorCode = null;
Exception logEx = null; Exception logEx = null;
@ -271,7 +361,6 @@ public class SqlUpdateImpl implements SqlUpdate {
MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap); MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap);
executeSql = mpSql.getSqlToExecute(); executeSql = mpSql.getSqlToExecute();
parameters = mpSql.getArgs(); parameters = mpSql.getArgs();
if (connection != null) { if (connection != null) {
ps = connection.prepareStatement(executeSql); ps = connection.prepareStatement(executeSql);
adaptor.addParameters(ps, parameters); adaptor.addParameters(ps, parameters);
@ -306,7 +395,6 @@ public class SqlUpdateImpl implements SqlUpdate {
} }
} }
private SqlUpdate positionalArg(Object arg) { private SqlUpdate positionalArg(Object arg) {
if (parameterList == null) { if (parameterList == null) {
parameterList = new ArrayList<>(); parameterList = new ArrayList<>();
@ -315,7 +403,6 @@ public class SqlUpdateImpl implements SqlUpdate {
return this; return this;
} }
private SqlUpdate namedArg(String argName, Object arg) { private SqlUpdate namedArg(String argName, Object arg) {
if (parameterMap == null) { if (parameterMap == null) {
parameterMap = new HashMap<>(); parameterMap = new HashMap<>();
@ -330,4 +417,14 @@ public class SqlUpdateImpl implements SqlUpdate {
private String booleanToString(Boolean b) { private String booleanToString(Boolean b) {
return b == null ? null : b ? "Y" : "N"; return b == null ? null : b ? "Y" : "N";
} }
private static class Batch {
private final List<Object> parameterList; // !null ==> traditional ? args
private final Map<String, Object> parameterMap; // !null ==> named :abc args
public Batch(List<Object> parameterList, Map<String, Object> parameterMap) {
this.parameterList = parameterList;
this.parameterMap = parameterMap;
}
}
} }