From 1c1a1ed55a5094fd5913f63a129b522ac2de7818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Mon, 1 Aug 2022 11:23:14 +0200 Subject: [PATCH] add batch methods to SqlUpdate --- gradle.properties | 2 +- .../org/xbib/jdbc/query/SqlInsertImpl.java | 8 +- .../java/org/xbib/jdbc/query/SqlUpdate.java | 6 + .../org/xbib/jdbc/query/SqlUpdateImpl.java | 109 +++++++++++++++++- 4 files changed, 114 insertions(+), 11 deletions(-) diff --git a/gradle.properties b/gradle.properties index 428c87e..02c7627 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = database -version = 0.0.3 +version = 0.0.4 org.gradle.warning.mode = ALL diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java index 53a75c1..f9c14c7 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlInsertImpl.java @@ -269,7 +269,7 @@ public class SqlInsertImpl implements SqlInsert { @Override public void insertBatch() { - int[] result = updateBatch(); + int[] result = insertBatchInternal(); for (int r : result) { // Tolerate SUCCESS_NO_INFO for older versions of Oracle if (r != 1 && r != Statement.SUCCESS_NO_INFO) { @@ -280,7 +280,7 @@ public class SqlInsertImpl implements SqlInsert { @Override public int[] insertBatchUnchecked() { - return updateBatch(); + return insertBatchInternal(); } @Override @@ -414,7 +414,7 @@ public class SqlInsertImpl implements SqlInsert { return pkArgName != null || pkSeqName != null || pkLong != null; } - private int[] updateBatch() { + private int[] insertBatchInternal() { batch(); if (batched == null || batched.size() == 0) { @@ -682,7 +682,7 @@ public class SqlInsertImpl implements SqlInsert { return b == null ? null : b ? "Y" : "N"; } - private class Batch { + private static class Batch { private final List parameterList; // !null ==> traditional ? args private final Map parameterMap; // !null ==> named :abc args diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java index 868f58b..1d2cb78 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdate.java @@ -77,6 +77,8 @@ public interface SqlUpdate { SqlUpdate apply(Apply apply); + SqlUpdate batch(); + /** * Execute the SQL update and return the number of rows was affected. */ @@ -90,6 +92,10 @@ public interface SqlUpdate { */ void update(int expectedRowsUpdated); + void updateBatch(); + + int[] updateBatchUnchecked(); + interface Apply { void apply(SqlUpdate update); } diff --git a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java index cfcc558..562472c 100644 --- a/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java +++ b/jdbc-query/src/main/java/org/xbib/jdbc/query/SqlUpdateImpl.java @@ -1,5 +1,6 @@ package org.xbib.jdbc.query; +import java.sql.Statement; import java.sql.Types; import org.xbib.jdbc.query.util.DebugSql; import org.xbib.jdbc.query.util.InternalStringReader; @@ -14,6 +15,7 @@ import java.sql.PreparedStatement; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,7 +36,9 @@ public class SqlUpdateImpl implements SqlUpdate { private final String sql; private final Options options; - + + private List batched; + private List parameterList; private Map parameterMap; @@ -247,6 +251,20 @@ public class SqlUpdateImpl implements SqlUpdate { 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 public int update() { return updateInternal(0); @@ -257,13 +275,85 @@ public class SqlUpdateImpl implements SqlUpdate { 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 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) { PreparedStatement ps = null; Metric metric = new Metric(log.isLoggable(Level.FINE)); - String executeSql = sql; Object[] parameters = null; - boolean isSuccess = false; String errorCode = null; Exception logEx = null; @@ -271,7 +361,6 @@ public class SqlUpdateImpl implements SqlUpdate { MixedParameterSql mpSql = new MixedParameterSql(sql, parameterList, parameterMap); executeSql = mpSql.getSqlToExecute(); parameters = mpSql.getArgs(); - if (connection != null) { ps = connection.prepareStatement(executeSql); adaptor.addParameters(ps, parameters); @@ -305,7 +394,6 @@ public class SqlUpdateImpl implements SqlUpdate { } } } - private SqlUpdate positionalArg(Object arg) { if (parameterList == null) { @@ -314,7 +402,6 @@ public class SqlUpdateImpl implements SqlUpdate { parameterList.add(arg); return this; } - private SqlUpdate namedArg(String argName, Object arg) { if (parameterMap == null) { @@ -330,4 +417,14 @@ public class SqlUpdateImpl implements SqlUpdate { private String booleanToString(Boolean b) { return b == null ? null : b ? "Y" : "N"; } + + private static class Batch { + private final List parameterList; // !null ==> traditional ? args + private final Map parameterMap; // !null ==> named :abc args + + public Batch(List parameterList, Map parameterMap) { + this.parameterList = parameterList; + this.parameterMap = parameterMap; + } + } }