From dc2b7ddb00310bbae191cd6a76b6d37b0eaed1e4 Mon Sep 17 00:00:00 2001 From: johnhx Date: Sat, 6 May 2017 21:38:17 +0800 Subject: [PATCH] .add getGeneratedKeys to provide ability to get batch insert's keys. --- src/main/java/org/j8ql/PStmt.java | 12 +++++++ src/main/java/org/j8ql/Runner.java | 4 +++ src/main/java/org/j8ql/RunnerException.java | 3 +- src/main/java/org/j8ql/RunnerImpl.java | 27 +++++++++++++-- src/main/java/org/j8ql/RunnerProxy.java | 6 ++++ .../org/j8ql/test/GetGeneratedKeysTest.java | 34 +++++++++++++++++++ src/test/java/org/j8ql/test/ReadmeTest.java | 2 +- src/test/java/org/j8ql/test/TsvTest.java | 4 +-- 8 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 src/test/java/org/j8ql/test/GetGeneratedKeysTest.java diff --git a/src/main/java/org/j8ql/PStmt.java b/src/main/java/org/j8ql/PStmt.java index a1a0b8a..08b0b6f 100644 --- a/src/main/java/org/j8ql/PStmt.java +++ b/src/main/java/org/j8ql/PStmt.java @@ -135,6 +135,18 @@ public int[] executeBatch(List valuesArray) throws RSQLException{ throw new RSQLException(e,sql); } } + + public List getGeneratedKeys() throws RSQLException{ + List list; + ResultSet rs; + try{ + rs = pstmt.getGeneratedKeys(); + list = db.buildResults(rs); + return list; + }catch (SQLException e) { + throw new RSQLException(e,sql); + } + } // --------- /Low Level Execute --------- // // --------- List --------- // diff --git a/src/main/java/org/j8ql/Runner.java b/src/main/java/org/j8ql/Runner.java index 1d9346d..3a80a6a 100644 --- a/src/main/java/org/j8ql/Runner.java +++ b/src/main/java/org/j8ql/Runner.java @@ -46,6 +46,8 @@ public interface Runner extends AutoCloseable { int[] executeBatch(InsertQuery builder); + List getGeneratedKeys(); + // --------- SQL Query APIs --------- // List list(Class cls, String sql, Object... values); @@ -84,6 +86,8 @@ public interface Runner extends AutoCloseable { // --------- Stmt Factory --------- // PStmt newPQuery(String sql); + + PStmt newPQuery(String sql, int autoGeneratedKeys); // --------- /Stmt Factory --------- // // --------- Transaction --------- // diff --git a/src/main/java/org/j8ql/RunnerException.java b/src/main/java/org/j8ql/RunnerException.java index 006b1cc..fcd1651 100644 --- a/src/main/java/org/j8ql/RunnerException.java +++ b/src/main/java/org/j8ql/RunnerException.java @@ -12,7 +12,8 @@ public class RunnerException extends BaseException{ public enum RunnerError implements BaseException.Error { DML_BUILDER_HAS_NO_RETURNING_CANNOT_BE_STREAMED("This UpdateQuery does not have any returning and therefore cannot be streamed or listed"), INCOMPLETE_INSERT_BUILDER_NO_COLUMNS_OR_VALUE_OBJECT("This inserBuilder does not contain columns or a values object, not sure what to insert."), - CANNOT_BATCH_DOES_NOT_HAVE_BATCH_VALUE("This builder cannot be batchExecuted because it does not contain a batchValue or batchValues"); + CANNOT_BATCH_DOES_NOT_HAVE_BATCH_VALUE("This builder cannot be batchExecuted because it does not contain a batchValue or batchValues"), + CANNOT_GETKEYS_DOES_NOT_HAVE_PREPARED_STATEMENT("This builder doesn't have PreparedStatement"); String msg; RunnerError(String msg) { diff --git a/src/main/java/org/j8ql/RunnerImpl.java b/src/main/java/org/j8ql/RunnerImpl.java index 96d53ca..c56e8c6 100644 --- a/src/main/java/org/j8ql/RunnerImpl.java +++ b/src/main/java/org/j8ql/RunnerImpl.java @@ -8,6 +8,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Statement; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -28,9 +29,11 @@ public class RunnerImpl implements Runner { // Since the db is final, it cannot be changed, so, safe to give public access. public final DB db; + private List batchResult; RunnerImpl(DB db, Connection con) { this.con = con; this.db = db; + this.batchResult = null; } // --------- Count --------- // @@ -155,12 +158,20 @@ public int[] executeBatch(InsertQuery builder) { if (builder.getBatchValues() == null && builder.getBatchObjects() == null){ throw new RunnerException(RunnerError.CANNOT_BATCH_DOES_NOT_HAVE_BATCH_VALUE); } + int[] r; String sql = db.sql(builder); List batchValues = db.batchValues(builder); - try (PStmt query = newPQuery(sql)) { - return query.executeBatch(batchValues); + try (PStmt query = newPQuery(sql, Statement.RETURN_GENERATED_KEYS)) { + r = query.executeBatch(batchValues); + this.batchResult = query.getGeneratedKeys(); + return r; } } + + @Override + public List getGeneratedKeys() { + return this.batchResult; + } // --------- /batch --------- // // --------- Private Helpers --------- // @@ -282,6 +293,18 @@ public PStmt newPQuery(String sql) { throw new RSQLException(e); } } + + @Override + public PStmt newPQuery(String sql, int autoGeneratedKeys) { + try { + PreparedStatement pstmt; + pstmt = con.prepareStatement(sql, autoGeneratedKeys); + + return new PStmt(db, sql, pstmt); + } catch (SQLException e) { + throw new RSQLException(e); + } + } // --------- /Stmt Factory --------- // // --------- Transaction --------- // diff --git a/src/main/java/org/j8ql/RunnerProxy.java b/src/main/java/org/j8ql/RunnerProxy.java index 7411eea..18e354e 100644 --- a/src/main/java/org/j8ql/RunnerProxy.java +++ b/src/main/java/org/j8ql/RunnerProxy.java @@ -87,6 +87,9 @@ public int[] executeBatch(InsertQuery builder) { return runner.executeBatch(builder); } + @Override + public List getGeneratedKeys() { return runner.getGeneratedKeys(); } + @Override public List list(Class cls, String sql, Object... values) { return runner.list(cls, sql, values); @@ -132,6 +135,9 @@ public PStmt newPQuery(String sql) { return runner.newPQuery(sql); } + @Override + public PStmt newPQuery(String sql, int autoGeneratedKeys) { return runner.newPQuery(sql, autoGeneratedKeys); } + @Override public Runner startTransaction() { return runner.startTransaction(); diff --git a/src/test/java/org/j8ql/test/GetGeneratedKeysTest.java b/src/test/java/org/j8ql/test/GetGeneratedKeysTest.java new file mode 100644 index 0000000..c2a70eb --- /dev/null +++ b/src/test/java/org/j8ql/test/GetGeneratedKeysTest.java @@ -0,0 +1,34 @@ +package org.j8ql.test; + +import org.j8ql.DB; +import org.j8ql.DBBuilder; +import org.j8ql.Record; +import org.j8ql.Runner; +import org.j8ql.query.InsertQuery; +import org.j8ql.query.Query; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.j8ql.query.Query.select; +import static org.junit.Assert.assertEquals; + +public class GetGeneratedKeysTest extends TestSupport { + @Test + public void testBatchInsertToFetchGeneratedKeys() { + DB db = new DBBuilder().build(dataSource); + + try (Runner runner = db.openRunner()) { + Object[][] data = {{"ticket 01 testBatchInsertToFetchGeneratedKeys"}, {"ticket 02 testBatchInsertToFetchGeneratedKeys"}}; + List batchValues = Stream.of(data).map(Arrays::asList).collect(Collectors.toList()); + InsertQuery insert = Query.insert("ticket").columns("subject"); + runner.executeBatch(insert.batchValues(batchValues)); + List generatedKeys = runner.getGeneratedKeys(); + assertEquals(2, runner.count(select("ticket"))); + assertEquals(2, generatedKeys.size()); + } + } +} diff --git a/src/test/java/org/j8ql/test/ReadmeTest.java b/src/test/java/org/j8ql/test/ReadmeTest.java index 9b89e40..3666133 100644 --- a/src/test/java/org/j8ql/test/ReadmeTest.java +++ b/src/test/java/org/j8ql/test/ReadmeTest.java @@ -177,7 +177,7 @@ public void fullTextSearchExample() { // NOTE 1: You can add third part (with the second ";") and in this case, it will be columnNameOrFunction;operator;valueFunction // NOTE 2: The first "columnName" can be a function, and in this case, it won't be escaped; // NOTE 3: the last ";to_tsquery(?)" allow to optionally add a function value to the query (which is what we need for tsv search). - SelectQuery tsvSelect = Query.select(Ticket.class).where("to_tsvector(ticket.subject);@@;to_tsquery(?)", "management"); + SelectQuery tsvSelect = Query.select(Ticket.class).where("to_tsvector(ticket.subject);@@;to_tsquery(?)", "manager"); System.out.println("sql: " + db.sql(tsvSelect)); // sql: select "ticket".* from "ticket" where to_tsvector(ticket.subject) @@ to_tsquery(?) diff --git a/src/test/java/org/j8ql/test/TsvTest.java b/src/test/java/org/j8ql/test/TsvTest.java index a753d83..1f2395b 100644 --- a/src/test/java/org/j8ql/test/TsvTest.java +++ b/src/test/java/org/j8ql/test/TsvTest.java @@ -30,11 +30,11 @@ public void simpleTsvSearch(){ } // raw SQL tsv search (postgres tsv search will match "management" with "manager", as both have the "manag" lexeme) - List recordsFromSql = runner.list("select subject from ticket where to_tsvector(subject) @@ to_tsquery(?)","management"); + List recordsFromSql = runner.list("select subject from ticket where to_tsvector(subject) @@ to_tsquery(?)","manager"); assertEquals(2, recordsFromSql.size()); // same, will - SelectQuery selectQuery = Query.select("ticket").columns("subject").where("to_tsvector(subject);@@;to_tsquery(?)","management"); + SelectQuery selectQuery = Query.select("ticket").columns("subject").where("to_tsvector(subject);@@;to_tsquery(?)","manager"); List recordsFromSelectQuery = runner.list(selectQuery); assertEquals(2, recordsFromSelectQuery.size());