From a8adb94d009a65bbfc6985e833bf2d3510382a20 Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 8 Dec 2025 20:24:35 -0500 Subject: Checkpoint commit for NamedPreparedStatement The checkpoint is that I attempted to patch the version of NamedPreparedStatement I had to support building external batches. This... didn't really work right; though it initially appeared to do so. I am going to rewrite NamedPreparedStatement to actually properly support this functionality instead of half-arsing it --- base/src/main/java/bjc/utils/misc/Binder.java | 8 + .../bjc/utils/misc/BoundPreparedStatement.java | 456 +++++++++++++++++++ .../bjc/utils/misc/NamedPreparedStatement.java | 499 ++------------------- .../main/java/bjc/utils/misc/ParamSnapshot.java | 33 ++ 4 files changed, 536 insertions(+), 460 deletions(-) create mode 100644 base/src/main/java/bjc/utils/misc/Binder.java create mode 100644 base/src/main/java/bjc/utils/misc/BoundPreparedStatement.java create mode 100644 base/src/main/java/bjc/utils/misc/ParamSnapshot.java (limited to 'base/src/main/java') diff --git a/base/src/main/java/bjc/utils/misc/Binder.java b/base/src/main/java/bjc/utils/misc/Binder.java new file mode 100644 index 0000000..e657592 --- /dev/null +++ b/base/src/main/java/bjc/utils/misc/Binder.java @@ -0,0 +1,8 @@ +package bjc.utils.misc; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +@FunctionalInterface interface Binder { + void bind(PreparedStatement ps, int index) throws SQLException; +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/misc/BoundPreparedStatement.java b/base/src/main/java/bjc/utils/misc/BoundPreparedStatement.java new file mode 100644 index 0000000..f87f4ec --- /dev/null +++ b/base/src/main/java/bjc/utils/misc/BoundPreparedStatement.java @@ -0,0 +1,456 @@ +package bjc.utils.misc; + +import java.math.BigDecimal; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Parent class for {@link NamedPreparedStatement} + * + * Used mostly for cases where you want to prepare the bindings independently of the actual query. + */ +public class BoundPreparedStatement { + protected final Map singles = new HashMap<>(); + protected final Map> lists = new HashMap<>(); + protected final List batch = new ArrayList<>(); + + private static List toBinders(Collection values, int sqlType, boolean forceType) { + ArrayList list = new ArrayList<>(values.size()); + for (Object v : values) { + final Object val = v; + if (forceType) { + list.add((ps, i) -> { + if (val == null) + ps.setNull(i, sqlType); + else + ps.setObject(i, val, sqlType); + }); + } else { + list.add((ps, i) -> ps.setObject(i, val)); + } + } + return list; + } + + /** + * Record the current set of parameters as a batch and start a new one. + * + * @return The prepared statement + */ + public BoundPreparedStatement addBatch() { + batch.add(new ParamSnapshot(new HashMap<>(singles), deepCopyLists(lists))); + return this; + } + + /** + * Clear the currently bound parameters. + * + * @return The prepared statement + */ + public BoundPreparedStatement clearParameters() { + singles.clear(); + lists.clear(); + return this; + } + + /** + * Bind a object as a parameter + * + * @param name The name of the parameter + * @param value The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setObject(String name, Object value) { + singles.put(name, (ps, i) -> ps.setObject(i, value)); + return this; + } + + /** + * Bind a object as a parameter + * + * @param name The name of the parameter + * @param value The value for the parameter + * @param sqlType The SQL type for the parameter (see {@link java.sql.Types}) + * @return The prepared statement + */ + public BoundPreparedStatement setObject(String name, Object value, int sqlType) { + singles.put(name, (ps, i) -> { + if (value == null) + ps.setNull(i, sqlType); + else + ps.setObject(i, value, sqlType); + }); + return this; + } + + /** + * Bind a null as a parameter + * + * @param name The name of the parameter + * @param sqlType The SQL type for the parameter (see {@link java.sql.Types}) + * @return The prepared statement + */ + public BoundPreparedStatement setNull(String name, int sqlType) { + singles.put(name, (ps, i) -> ps.setNull(i, sqlType)); + return this; + } + + /** + * Bind a boolean as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setBoolean(String n, boolean x) { + singles.put(n, (ps, i) -> ps.setBoolean(i, x)); + return this; + } + + /** + * Bind a byte as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setByte(String n, byte x) { + singles.put(n, (ps, i) -> ps.setByte(i, x)); + return this; + } + + /** + * Bind a short as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setShort(String n, short x) { + singles.put(n, (ps, i) -> ps.setShort(i, x)); + return this; + } + + /** + * Bind a int as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setInt(String n, int x) { + singles.put(n, (ps, i) -> ps.setInt(i, x)); + return this; + } + + /** + * Bind a long as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setLong(String n, long x) { + singles.put(n, (ps, i) -> ps.setLong(i, x)); + return this; + } + + /** + * Bind a float as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setFloat(String n, float x) { + singles.put(n, (ps, i) -> ps.setFloat(i, x)); + return this; + } + + /** + * Bind a double as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setDouble(String n, double x) { + singles.put(n, (ps, i) -> ps.setDouble(i, x)); + return this; + } + + /** + * Bind a {@link BigDecimal} as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setBigDecimal(String n, BigDecimal x) { + singles.put(n, (ps, i) -> ps.setBigDecimal(i, x)); + return this; + } + + /** + * Bind a string as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setString(String n, String x) { + singles.put(n, (ps, i) -> ps.setString(i, x)); + return this; + } + + /** + * Bind a byte-array as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setBytes(String n, byte[] x) { + singles.put(n, (ps, i) -> ps.setBytes(i, x)); + return this; + } + + /** + * Bind a date as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setDate(String n, Date x) { + singles.put(n, (ps, i) -> ps.setDate(i, x)); + return this; + } + + /** + * Bind a date as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @param c The calendar for the date + * @return The prepared statement + */ + public BoundPreparedStatement setDate(String n, Date x, Calendar c) { + singles.put(n, (ps, i) -> ps.setDate(i, x, c)); + return this; + } + + /** + * Bind a time-value as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setTime(String n, Time x) { + singles.put(n, (ps, i) -> ps.setTime(i, x)); + return this; + } + + /** + * Bind a time-value as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @param c The calendar for the time + * @return The prepared statement + */ + public BoundPreparedStatement setTime(String n, Time x, Calendar c) { + singles.put(n, (ps, i) -> ps.setTime(i, x, c)); + return this; + } + + /** + * Bind a timestamp as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setTimestamp(String n, Timestamp x) { + singles.put(n, (ps, i) -> ps.setTimestamp(i, x)); + return this; + } + + /** + * Bind a timestamp as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @param c The calendar for the timestamp + * @return The prepared statement + */ + public BoundPreparedStatement setTimestamp(String n, Timestamp x, Calendar c) { + singles.put(n, (ps, i) -> ps.setTimestamp(i, x, c)); + return this; + } + + /** + * Bind a {@link Array} as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setArray(String n, Array x) { + singles.put(n, (ps, i) -> ps.setArray(i, x)); + return this; + } + + /** + * Bind a {@link Blob} as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setBlob(String n, Blob x) { + singles.put(n, (ps, i) -> ps.setBlob(i, x)); + return this; + } + + /** + * Bind a {@link Clob} as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setClob(String n, Clob x) { + singles.put(n, (ps, i) -> ps.setClob(i, x)); + return this; + } + + /** + * Bind a URL as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @return The prepared statement + */ + public BoundPreparedStatement setURL(String n, java.net.URL x) { + singles.put(n, (ps, i) -> ps.setURL(i, x)); + return this; + } + + /** + * Bind a ASCII stream as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @param len The length of the stream + * @return The prepared statement + */ + public BoundPreparedStatement setAsciiStream(String n, java.io.InputStream x, int len) { + singles.put(n, (ps, i) -> ps.setAsciiStream(i, x, len)); + return this; + } + + /** + * Bind a binary stream as a parameter + * + * @param n The name of the parameter + * @param x The value for the parameter + * @param len The length of the stream + * @return The prepared statement + */ + public BoundPreparedStatement setBinaryStream(String n, java.io.InputStream x, int len) { + singles.put(n, (ps, i) -> ps.setBinaryStream(i, x, len)); + return this; + } + + /** + * Bind a character stream as a parameter + * + * @param n The name of the parameter + * @param r The value for the parameter + * @param len The length of the stream + * @return The prepared statement + */ + public BoundPreparedStatement setCharacterStream(String n, java.io.Reader r, int len) { + singles.put(n, (ps, i) -> ps.setCharacterStream(i, r, len)); + return this; + } + + /** + * Bind a list parameter to the statement + * + * @param name The name of the list parameter + * @param values The values for the list parameter + * @return The prepared statement + */ + public BoundPreparedStatement setList(String name, Collection values) { + lists.put(name, toBinders(values, Types.OTHER, false)); + return this; + } + + /** + * Bind a list parameter to the statement + * + * @param name The name of the list parameter + * @param values The values for the list parameter + * @return The prepared statement + */ + public BoundPreparedStatement setList(String name, Object... values) { + lists.put(name, toBinders(Arrays.asList(values), Types.OTHER, false)); + return this; + } + + /** + * Explicit SQL type for all elements (useful if nulls might appear). + * + * @param name The name of the list parameter + * @param sqlType The SQL type for the parameters + * @param values The values for the list parameter + * @return The prepared statement + */ + public BoundPreparedStatement setList(String name, int sqlType, Collection values) { + lists.put(name, toBinders(values, sqlType, true)); + return this; + } + + /** + * Explicit SQL type for all elements (useful if nulls might appear). + * + * @param name The name of the list parameter + * @param sqlType The SQL type for the parameters + * @param values The values for the list parameter + * @return The prepared statement + */ + public BoundPreparedStatement setList(String name, int sqlType, Object... values) { + lists.put(name, toBinders(Arrays.asList(values), sqlType, true)); + return this; + } + + private static Map> deepCopyLists(Map> src) { + Map> m = new HashMap<>(src.size()); + for (Map.Entry> e : src.entrySet()) { + m.put(e.getKey(), new ArrayList<>(e.getValue())); + } + return m; + } + + /** + * Create a new {@link BoundPreparedStatement} + */ + public BoundPreparedStatement() { + + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/misc/NamedPreparedStatement.java b/base/src/main/java/bjc/utils/misc/NamedPreparedStatement.java index b35029e..4389293 100644 --- a/base/src/main/java/bjc/utils/misc/NamedPreparedStatement.java +++ b/base/src/main/java/bjc/utils/misc/NamedPreparedStatement.java @@ -1,9 +1,8 @@ package bjc.utils.misc; -import java.math.BigDecimal; import java.sql.*; -import java.sql.Date; import java.util.*; +import java.util.function.Function; /** * NamedPreparedStatement @@ -30,8 +29,7 @@ import java.util.*; * try (ResultSet rs = ps.executeQuery()) { ... } * } */ -public final class NamedPreparedStatement implements AutoCloseable { - +public class NamedPreparedStatement extends BoundPreparedStatement implements AutoCloseable { /* ---------- Construction ---------- */ private final Connection conn; @@ -46,19 +44,13 @@ public final class NamedPreparedStatement implements AutoCloseable { private PreparedStatement currentPs; private String lastCompiledSql; - // parameter values - private final Map singles = new HashMap<>(); - private final Map> lists = new HashMap<>(); - - // simple batch snapshots - private final List batch = new ArrayList<>(); - // empty-list behavior private EmptyListMode emptyListMode = EmptyListMode.AS_NULL; private String emptyListLiteral = "NULL"; private NamedPreparedStatement(Connection conn, String sql, Integer autoGeneratedKeys, Integer resultSetType, Integer resultSetConcurrency) { + super(); this.conn = Objects.requireNonNull(conn, "conn"); this.originalSql = Objects.requireNonNull(sql, "sql"); this.tokens = parse(sql); @@ -144,7 +136,7 @@ public final class NamedPreparedStatement implements AutoCloseable { buildAndBind(); return currentPs; } - + /* ---------- Execution ---------- */ /** @@ -209,26 +201,38 @@ public final class NamedPreparedStatement implements AutoCloseable { } /** - * Record the current set of parameters as a batch and start a new one. - * - * @return The prepared statement + * Execute a batch from a recorded snapshot + * @param record + * @return The number of rows updated by each batch + * @throws SQLException */ - public NamedPreparedStatement addBatch() { - batch.add(new ParamSnapshot(new HashMap<>(singles), deepCopyLists(lists))); - return this; - } + public List executeBatchFromRecord(BoundPreparedStatement record) throws SQLException { + if (record.batch.isEmpty()) { + buildAndBind(); + int[] batchRes = currentPs.executeBatch(); + List resList = new ArrayList<>(batchRes.length); + for (int i : batchRes) resList.add(i); + return resList; + } + + List counts = new ArrayList<>(batch.size()); + List binders = buildPreparedStatement(record); - /** - * Clear the currently bound parameters. - * - * @return The prepared statement - */ - public NamedPreparedStatement clearParameters() { - singles.clear(); - lists.clear(); - return this; - } + for (ParamSnapshot snap : record.batch) { + // restore snapshot + this.singles.clear(); + this.lists.clear(); + this.singles.putAll(snap.singles); + this.lists.putAll(snap.lists); + + bindPreparedStatement(binders); + counts.add(currentPs.executeUpdate()); + } + record.batch.clear(); + return counts; + } + /** * Get the generated keys for this prepared statement. * @@ -298,381 +302,11 @@ public final class NamedPreparedStatement implements AutoCloseable { /* ---------- Binding (single) ---------- */ - /** - * Bind a object as a parameter - * - * @param name The name of the parameter - * @param value The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setObject(String name, Object value) { - singles.put(name, (ps, i) -> ps.setObject(i, value)); - return this; - } - - /** - * Bind a object as a parameter - * - * @param name The name of the parameter - * @param value The value for the parameter - * @param sqlType The SQL type for the parameter (see {@link java.sql.Types}) - * @return The prepared statement - */ - public NamedPreparedStatement setObject(String name, Object value, int sqlType) { - singles.put(name, (ps, i) -> { - if (value == null) - ps.setNull(i, sqlType); - else - ps.setObject(i, value, sqlType); - }); - return this; - } - /** - * Bind a null as a parameter - * - * @param name The name of the parameter - * @param sqlType The SQL type for the parameter (see {@link java.sql.Types}) - * @return The prepared statement - */ - public NamedPreparedStatement setNull(String name, int sqlType) { - singles.put(name, (ps, i) -> ps.setNull(i, sqlType)); - return this; - } - - /** - * Bind a boolean as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setBoolean(String n, boolean x) { - singles.put(n, (ps, i) -> ps.setBoolean(i, x)); - return this; - } - - /** - * Bind a byte as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setByte(String n, byte x) { - singles.put(n, (ps, i) -> ps.setByte(i, x)); - return this; - } - - /** - * Bind a short as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setShort(String n, short x) { - singles.put(n, (ps, i) -> ps.setShort(i, x)); - return this; - } - - /** - * Bind a int as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setInt(String n, int x) { - singles.put(n, (ps, i) -> ps.setInt(i, x)); - return this; - } - - /** - * Bind a long as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setLong(String n, long x) { - singles.put(n, (ps, i) -> ps.setLong(i, x)); - return this; - } - - /** - * Bind a float as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setFloat(String n, float x) { - singles.put(n, (ps, i) -> ps.setFloat(i, x)); - return this; - } - - /** - * Bind a double as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setDouble(String n, double x) { - singles.put(n, (ps, i) -> ps.setDouble(i, x)); - return this; - } - - /** - * Bind a {@link BigDecimal} as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setBigDecimal(String n, BigDecimal x) { - singles.put(n, (ps, i) -> ps.setBigDecimal(i, x)); - return this; - } - - /** - * Bind a string as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setString(String n, String x) { - singles.put(n, (ps, i) -> ps.setString(i, x)); - return this; - } - - /** - * Bind a byte-array as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setBytes(String n, byte[] x) { - singles.put(n, (ps, i) -> ps.setBytes(i, x)); - return this; - } - - /** - * Bind a date as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setDate(String n, Date x) { - singles.put(n, (ps, i) -> ps.setDate(i, x)); - return this; - } - - /** - * Bind a date as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @param c The calendar for the date - * @return The prepared statement - */ - public NamedPreparedStatement setDate(String n, Date x, Calendar c) { - singles.put(n, (ps, i) -> ps.setDate(i, x, c)); - return this; - } - - /** - * Bind a time-value as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setTime(String n, Time x) { - singles.put(n, (ps, i) -> ps.setTime(i, x)); - return this; - } - - /** - * Bind a time-value as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @param c The calendar for the time - * @return The prepared statement - */ - public NamedPreparedStatement setTime(String n, Time x, Calendar c) { - singles.put(n, (ps, i) -> ps.setTime(i, x, c)); - return this; - } - - /** - * Bind a timestamp as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setTimestamp(String n, Timestamp x) { - singles.put(n, (ps, i) -> ps.setTimestamp(i, x)); - return this; - } - - /** - * Bind a timestamp as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @param c The calendar for the timestamp - * @return The prepared statement - */ - public NamedPreparedStatement setTimestamp(String n, Timestamp x, Calendar c) { - singles.put(n, (ps, i) -> ps.setTimestamp(i, x, c)); - return this; - } - - /** - * Bind a {@link Array} as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setArray(String n, Array x) { - singles.put(n, (ps, i) -> ps.setArray(i, x)); - return this; - } - - /** - * Bind a {@link Blob} as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setBlob(String n, Blob x) { - singles.put(n, (ps, i) -> ps.setBlob(i, x)); - return this; - } - - /** - * Bind a {@link Clob} as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setClob(String n, Clob x) { - singles.put(n, (ps, i) -> ps.setClob(i, x)); - return this; - } - - /** - * Bind a URL as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @return The prepared statement - */ - public NamedPreparedStatement setURL(String n, java.net.URL x) { - singles.put(n, (ps, i) -> ps.setURL(i, x)); - return this; - } - - /** - * Bind a ASCII stream as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @param len The length of the stream - * @return The prepared statement - */ - public NamedPreparedStatement setAsciiStream(String n, java.io.InputStream x, int len) { - singles.put(n, (ps, i) -> ps.setAsciiStream(i, x, len)); - return this; - } - - /** - * Bind a binary stream as a parameter - * - * @param n The name of the parameter - * @param x The value for the parameter - * @param len The length of the stream - * @return The prepared statement - */ - public NamedPreparedStatement setBinaryStream(String n, java.io.InputStream x, int len) { - singles.put(n, (ps, i) -> ps.setBinaryStream(i, x, len)); - return this; - } - - /** - * Bind a character stream as a parameter - * - * @param n The name of the parameter - * @param r The value for the parameter - * @param len The length of the stream - * @return The prepared statement - */ - public NamedPreparedStatement setCharacterStream(String n, java.io.Reader r, int len) { - singles.put(n, (ps, i) -> ps.setCharacterStream(i, r, len)); - return this; - } /* ---------- Binding (lists) ---------- */ - /** - * Bind a list parameter to the statement - * - * @param name The name of the list parameter - * @param values The values for the list parameter - * @return The prepared statement - */ - public NamedPreparedStatement setList(String name, Collection values) { - lists.put(name, toBinders(values, Types.OTHER, false)); - return this; - } - - /** - * Bind a list parameter to the statement - * - * @param name The name of the list parameter - * @param values The values for the list parameter - * @return The prepared statement - */ - public NamedPreparedStatement setList(String name, Object... values) { - lists.put(name, toBinders(Arrays.asList(values), Types.OTHER, false)); - return this; - } - - /** - * Explicit SQL type for all elements (useful if nulls might appear). - * - * @param name The name of the list parameter - * @param sqlType The SQL type for the parameters - * @param values The values for the list parameter - * @return The prepared statement - */ - public NamedPreparedStatement setList(String name, int sqlType, Collection values) { - lists.put(name, toBinders(values, sqlType, true)); - return this; - } - - /** - * Explicit SQL type for all elements (useful if nulls might appear). - * - * @param name The name of the list parameter - * @param sqlType The SQL type for the parameters - * @param values The values for the list parameter - * @return The prepared statement - */ - public NamedPreparedStatement setList(String name, int sqlType, Object... values) { - lists.put(name, toBinders(Arrays.asList(values), sqlType, true)); - return this; - } + /* ---------- Build & bind ---------- */ @@ -689,6 +323,10 @@ public final class NamedPreparedStatement implements AutoCloseable { } private List buildPreparedStatement() throws SQLException { + return buildPreparedStatement(this); + } + + private List buildPreparedStatement(BoundPreparedStatement record) throws SQLException { StringBuilder sb = new StringBuilder(originalSql.length() + 32); List bindersInOrder = new ArrayList<>(32); @@ -742,7 +380,7 @@ public final class NamedPreparedStatement implements AutoCloseable { } return bindersInOrder; } - + /* ---------- Parser ---------- */ private interface Token { @@ -875,65 +513,6 @@ public final class NamedPreparedStatement implements AutoCloseable { /* ---------- Helpers ---------- */ - @FunctionalInterface - private interface Binder { - void bind(PreparedStatement ps, int index) throws SQLException; - } - - private static List toBinders(Collection values, int sqlType, boolean forceType) { - ArrayList list = new ArrayList<>(values.size()); - for (Object v : values) { - final Object val = v; - if (forceType) { - list.add((ps, i) -> { - if (val == null) - ps.setNull(i, sqlType); - else - ps.setObject(i, val, sqlType); - }); - } else { - list.add((ps, i) -> ps.setObject(i, val)); - } - } - return list; - } - - private static Map> deepCopyLists(Map> src) { - Map> m = new HashMap<>(src.size()); - for (Map.Entry> e : src.entrySet()) { - m.put(e.getKey(), new ArrayList<>(e.getValue())); - } - return m; - } - - private static class ParamSnapshot { - public Map singles; - public Map> lists; - - public ParamSnapshot(Map singles, Map> lists) { - super(); - this.singles = singles; - this.lists = lists; - } - - @Override - public int hashCode() { - return Objects.hash(lists, singles); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ParamSnapshot other = (ParamSnapshot) obj; - return Objects.equals(lists, other.lists) && Objects.equals(singles, other.singles); - } - } - private enum EmptyListMode { AS_NULL, AS_CUSTOM_LITERAL } diff --git a/base/src/main/java/bjc/utils/misc/ParamSnapshot.java b/base/src/main/java/bjc/utils/misc/ParamSnapshot.java new file mode 100644 index 0000000..5f2c756 --- /dev/null +++ b/base/src/main/java/bjc/utils/misc/ParamSnapshot.java @@ -0,0 +1,33 @@ +package bjc.utils.misc; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +class ParamSnapshot { + public Map singles; + public Map> lists; + + public ParamSnapshot(Map singles, Map> lists) { + super(); + this.singles = singles; + this.lists = lists; + } + + @Override + public int hashCode() { + return Objects.hash(lists, singles); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ParamSnapshot other = (ParamSnapshot) obj; + return Objects.equals(lists, other.lists) && Objects.equals(singles, other.singles); + } +} \ No newline at end of file -- cgit v1.2.3