From 023bd15f06d68c25e22fc5cfe0715d31280f621e Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 1 Nov 2024 21:43:00 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e132180..5b5660e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ xyz.zhouxy.jdbc simple-jdbc - 1.0.0-SNAPSHOT + 1.0.0-alpha 8 @@ -18,7 +18,7 @@ xyz.zhouxy.plusone plusone-commons - 1.0.0-SNAPSHOT + 1.0.0-alpha From f5cef8f7307c1ff3b53d6f5d18b2189eee874bfe Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 2 Nov 2024 00:14:30 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=94=B9=E4=B8=BA=E4=BD=BF=E7=94=A8=20H2?= =?UTF-8?q?=20=E8=BF=9B=E8=A1=8C=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 25 +- .../jdbc/test/SimpleJdbcTemplateTests.java | 306 +++++++++++------- 2 files changed, 198 insertions(+), 133 deletions(-) diff --git a/pom.xml b/pom.xml index 5b5660e..b89cd4f 100644 --- a/pom.xml +++ b/pom.xml @@ -35,28 +35,11 @@ test + - com.zaxxer - HikariCP - 4.0.3 - - - org.slf4j - slf4j-api - - - test - - - org.postgresql - postgresql - 42.3.8 - - - org.checkerframework - checker-qual - - + com.h2database + h2 + 2.2.224 test diff --git a/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java b/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java index 1d10938..1658586 100644 --- a/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java +++ b/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java @@ -1,98 +1,142 @@ package xyz.zhouxy.jdbc.test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static xyz.zhouxy.jdbc.ParamBuilder.*; -import static xyz.zhouxy.plusone.commons.sql.JdbcSql.IN; +import static xyz.zhouxy.plusone.commons.sql.JdbcSql.*; import java.sql.SQLException; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -import javax.sql.DataSource; +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; import xyz.zhouxy.jdbc.DbRecord; import xyz.zhouxy.jdbc.RowMapper; import xyz.zhouxy.jdbc.SimpleJdbcTemplate; import xyz.zhouxy.jdbc.SimpleJdbcTemplate.JdbcExecutor; import xyz.zhouxy.plusone.commons.sql.SQL; -import xyz.zhouxy.plusone.commons.util.ArrayTools; import xyz.zhouxy.plusone.commons.util.IdGenerator; import xyz.zhouxy.plusone.commons.util.IdWorker; -import xyz.zhouxy.plusone.commons.util.Numbers; class SimpleJdbcTemplateTests { private static final Logger log = LoggerFactory.getLogger(SimpleJdbcTemplateTests.class); - private static final DataSource dataSource; - private static final SimpleJdbcTemplate jdbcTemplate; static { - HikariConfig config = new HikariConfig(); - config.setJdbcUrl("jdbc:postgresql://localhost:5432/plusone"); - config.setUsername("postgres"); - config.setPassword("zhouxy108"); - config.setMaximumPoolSize(8); - config.setConnectionTimeout(1000000); - dataSource = new HikariDataSource(config); + JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=FALSE;MODE=MySQL"); + dataSource.setUser("sa"); + dataSource.setPassword(""); jdbcTemplate = new SimpleJdbcTemplate(dataSource); } + @BeforeAll + static void setUp() throws SQLException { + jdbcTemplate.update("CREATE TABLE sys_account (" + + "\n" + " id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY" + + "\n" + " ,username VARCHAR(255) NOT NULL" + + "\n" + " ,account_status VARCHAR(2) NOT NULL" + + "\n" + " ,create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP" + + "\n" + " ,created_by BIGINT NOT NULL" + + "\n" + " ,update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP" + + "\n" + " ,updated_by BIGINT DEFAULT NULL" + + "\n" + " ,version BIGINT NOT NULL DEFAULT 0" + + "\n" + ")"); + jdbcTemplate.batchUpdate("INSERT INTO sys_account(id, username, account_status, created_by) VALUES (?, ?, ?, ?)", Lists.newArrayList( + buildParams(2L, "zhouxy2", "0", 108L), + buildParams(3L, "zhouxy3", "0", 108L), + buildParams(4L, "zhouxy4", "0", 108L), + buildParams(5L, "zhouxy5", "0", 108L), + buildParams(6L, "zhouxy6", "0", 108L), + buildParams(7L, "zhouxy7", "0", 108L), + buildParams(8L, "zhouxy8", "0", 108L), + buildParams(9L, "zhouxy9", "0", 108L) + ), 10); + jdbcTemplate.batchUpdate("INSERT INTO sys_account(id, username, account_status, created_by, create_time, update_time, version) VALUES (?, ?, ?, ?, ?, ?, ?)", Lists.newArrayList( + buildParams(10L, "zhouxy10", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 31), + buildParams(11L, "zhouxy11", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 28), + buildParams(12L, "zhouxy12", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 25), + buildParams(13L, "zhouxy13", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 22), + buildParams(14L, "zhouxy14", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 19), + buildParams(15L, "zhouxy15", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 16), + buildParams(16L, "zhouxy16", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 13), + buildParams(17L, "zhouxy17", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 10), + buildParams(18L, "zhouxy18", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 7), + buildParams(19L, "zhouxy19", "1", 118L, LocalDate.of(2000, 1, 1), LocalDate.of(2000, 1, 29), 0) + ), 10); + jdbcTemplate.update("INSERT INTO sys_account(id, username, account_status, created_by, create_time, updated_by, update_time, version) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + buildParams(20L, "zhouxy20", "2", 118L, LocalDateTime.of(2008, 8, 8, 20, 8), 31L, LocalDateTime.now(), 88L)); + } + @Test void testQuery() throws SQLException { - Object[] ids = buildParams(22915, 22916, 22917, 22918, 22919, 22920, 22921); + Object[] ids = buildParams(5, 9, 13, 14, 17, 20, 108); String sql = SQL.newJdbcSql() - .SELECT("*") - .FROM("test_table") + .SELECT("id", "username", "account_status") + .FROM("sys_account") .WHERE(IN("id", ids)) .toString(); log.info(sql); List rs = jdbcTemplate.queryRecordList(sql, ids); - assertNotNull(rs); - for (DbRecord baseEntity : rs) { - // log.info("id: {}", baseEntity.getValueAsString("id")); // NOSONAR - log.info(baseEntity.toString()); - assertTrue(baseEntity.getValueAsString("username").isPresent()); + for (DbRecord dbRecord : rs) { + log.info("{}", dbRecord); } + assertEquals( + Lists.newArrayList( + new DbRecord(ImmutableMap.of("id", 5L, "account_status", "0", "username", "zhouxy5")), + new DbRecord(ImmutableMap.of("id", 9L, "account_status", "0", "username", "zhouxy9")), + new DbRecord(ImmutableMap.of("id", 13L, "account_status", "1", "username", "zhouxy13")), + new DbRecord(ImmutableMap.of("id", 14L, "account_status", "1", "username", "zhouxy14")), + new DbRecord(ImmutableMap.of("id", 17L, "account_status", "1", "username", "zhouxy17")), + new DbRecord(ImmutableMap.of("id", 20L, "account_status", "2", "username", "zhouxy20")) + ), + rs + ); } @Test void testInsert() throws SQLException { - List keys = jdbcTemplate.update( - "INSERT INTO base_table(status, created_by) VALUES (?, ?)", - buildParams(1, 886L), - RowMapper.RECORD_MAPPER); + List> keys = jdbcTemplate.update( + "INSERT INTO sys_account(username, account_status, created_by) VALUES (?, ?, ?), (?, ?, ?)", + buildParams("zhouxy21", "2", 123L, "code22", '2', 456L), + RowMapper.HASH_MAP_MAPPER); log.info("keys: {}", keys); - assertEquals(1, keys.size()); - DbRecord result = keys.get(0); - assertEquals(1, result.getValueAsInt("status").getAsInt()); - assertEquals(886L, result.getValueAsLong("created_by").getAsLong()); - assertTrue(result.get("id").isPresent()); + assertEquals(2, keys.size()); + for (Map key : keys) { + assertTrue(key.containsKey("id")); + assertInstanceOf(Long.class, key.get("id")); + assertTrue(key.containsKey("create_time")); + assertInstanceOf(Date.class, key.get("create_time")); + } } @Test void testUpdate() throws SQLException { List keys = jdbcTemplate.update( - "UPDATE base_table SET status = ?, version = version + 1, update_time = now(), updated_by = ? WHERE id = ? AND version = ?", - buildParams(2, 886, 571328822575109L, 0), + "UPDATE sys_account SET account_status = ?, version = version + 1, update_time = now(), updated_by = ? WHERE id = ? AND version = ?", + buildParams("7", 886L, 20L, 88L), RowMapper.RECORD_MAPPER); + assertEquals(1, keys.size()); log.info("keys: {}", keys); + keys = jdbcTemplate.update( + "UPDATE sys_account SET account_status = ?, version = version + 1, update_time = now(), updated_by = ? WHERE id = ? AND version = ?", + buildParams("-1", 886L, 20L, 88L), + RowMapper.RECORD_MAPPER); + assertEquals(0, keys.size()); } final IdWorker idGenerator = IdGenerator.getSnowflakeIdGenerator(0); @@ -104,8 +148,8 @@ class SimpleJdbcTemplateTests { long id = this.idGenerator.nextId(); try { jdbcTemplate.executeTransaction((JdbcExecutor jdbc) -> { - jdbc.update("INSERT INTO base_table (id, created_by, create_time, status) VALUES (?, ?, ?, ?)", - buildParams(id, 100, LocalDateTime.now(), 0)); + jdbc.update("INSERT INTO sys_account (id, username, created_by, create_time, account_status) VALUES (?, ?, ?, ?, ?)", + buildParams(id, "testTransaction1", 100, LocalDateTime.now(), "55")); throw new NullPointerException(); }); } @@ -113,7 +157,7 @@ class SimpleJdbcTemplateTests { // ignore } Optional> first = jdbcTemplate - .queryFirst("SELECT * FROM base_table WHERE id = ?", buildParams(id)); + .queryFirst("SELECT * FROM sys_account WHERE id = ?", buildParams(id)); log.info("first: {}", first); assertTrue(!first.isPresent()); } @@ -122,12 +166,12 @@ class SimpleJdbcTemplateTests { { long id = this.idGenerator.nextId(); jdbcTemplate.executeTransaction(jdbc -> { - jdbc.update("INSERT INTO base_table (id, created_by, create_time, status) VALUES (?, ?, ?, ?)", - buildParams(id, 101, LocalDateTime.now(), 0)); + jdbc.update("INSERT INTO sys_account (id, username, created_by, create_time, account_status) VALUES (?, ?, ?, ?, ?)", + buildParams(id, "testTransaction2", 101, LocalDateTime.now(), "55")); }); Optional> first = jdbcTemplate - .queryFirst("SELECT * FROM base_table WHERE id = ?", buildParams(id)); + .queryFirst("SELECT * FROM sys_account WHERE id = ?", buildParams(id)); log.info("first: {}", first); assertTrue(first.isPresent()); } @@ -137,8 +181,8 @@ class SimpleJdbcTemplateTests { long id = this.idGenerator.nextId(); try { jdbcTemplate.commitIfTrue(jdbc -> { - jdbc.update("INSERT INTO base_table (id, created_by, create_time, status) VALUES (?, ?, ?, ?)", - buildParams(id, 102, LocalDateTime.now(), 0)); + jdbc.update("INSERT INTO sys_account (id, username, created_by, create_time, account_status) VALUES (?, ?, ?, ?, ?)", + buildParams(id, "testTransaction3", 102, LocalDateTime.now(), "55")); throw new NullPointerException(); }); } @@ -146,7 +190,7 @@ class SimpleJdbcTemplateTests { // ignore } Optional> first = jdbcTemplate - .queryFirst("SELECT * FROM base_table WHERE id = ?", buildParams(id)); + .queryFirst("SELECT * FROM sys_account WHERE id = ?", buildParams(id)); log.info("first: {}", first); assertTrue(!first.isPresent()); } @@ -155,13 +199,13 @@ class SimpleJdbcTemplateTests { { long id = this.idGenerator.nextId(); jdbcTemplate.commitIfTrue(jdbc -> { - jdbc.update("INSERT INTO base_table (id, created_by, create_time, status) VALUES (?, ?, ?, ?)", - buildParams(id, 103, LocalDateTime.now(), 0)); + jdbc.update("INSERT INTO sys_account (id, username, created_by, create_time, account_status) VALUES (?, ?, ?, ?, ?)", + buildParams(id, "testTransaction4", 103, LocalDateTime.now(), "55")); return false; }); Optional> first = jdbcTemplate - .queryFirst("SELECT * FROM base_table WHERE id = ?", buildParams(id)); + .queryFirst("SELECT * FROM sys_account WHERE id = ?", buildParams(id)); log.info("first: {}", first); assertTrue(!first.isPresent()); } @@ -170,72 +214,57 @@ class SimpleJdbcTemplateTests { { long id = this.idGenerator.nextId(); jdbcTemplate.commitIfTrue(jdbc -> { - jdbc.update("INSERT INTO base_table (id, created_by, create_time, status) VALUES (?, ?, ?, ?)", - buildParams(id, 104, LocalDateTime.now(), 0)); + jdbc.update("INSERT INTO sys_account (id, username, created_by, create_time, account_status) VALUES (?, ?, ?, ?, ?)", + buildParams(id, "testTransaction5", 104, LocalDateTime.now(), "55")); return true; }); Optional> first = jdbcTemplate - .queryFirst("SELECT * FROM base_table WHERE id = ?", buildParams(id)); + .queryFirst("SELECT * FROM sys_account WHERE id = ?", buildParams(id)); log.info("first: {}", first); assertTrue(first.isPresent()); } } - @Test - void testBatch() throws Exception { - - Random random = ThreadLocalRandom.current(); - - LocalDate handleDate = LocalDate.of(1949, 10, 1); - List datas = Lists.newArrayList(); - while (handleDate.isBefore(LocalDate.of(2008, 8, 8))) { - DbRecord r1 = new DbRecord(); - r1.put("username", "张三2"); - r1.put("usage_date", handleDate); - r1.put("usage_duration", random.nextInt(500)); - datas.add(r1); - DbRecord r2 = new DbRecord(); - r2.put("username", "李四2"); - r2.put("usage_date", handleDate); - r2.put("usage_duration", random.nextInt(500)); - datas.add(r2); - - handleDate = handleDate.plusDays(1L); - } - try { - List result = jdbcTemplate.batchUpdate( - "insert into test_table (username, usage_date, usage_duration) values (?,?,?)", - buildBatchParams(datas, item -> buildParams( - item.getValueAsString("username"), - item.getValueAsString("usage_date"), - item.getValueAsString("usage_duration"))), - 400); - long sum = Numbers.sum(ArrayTools.concatIntArray(result)); - assertEquals(datas.size(), sum); - log.info("sum: {}", sum); - } - catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - @Test void testBean() throws Exception { - Optional t = jdbcTemplate.queryFirst( - "SELECT * FROM test_table WHERE id = ?", - buildParams(22915), - RowMapper.beanRowMapper(TestBean.class)); - log.info("t: {}", t); + Optional t = jdbcTemplate.queryFirst( + "SELECT * FROM sys_account WHERE id = ?", + buildParams(18L), + RowMapper.beanRowMapper(AccountPO.class)); + assertEquals( + new AccountPO(18L, "zhouxy18", "1", + LocalDateTime.of(2000, 1, 1, 0, 0), 118L, + LocalDateTime.of(2000, 1, 29, 0, 0), null, 7L), + t.get()); + log.info("{}", t); } } -class TestBean { +class AccountPO { Long id; String username; - LocalDate usageDate; - Long usageDuration; + String accountStatus; + LocalDateTime createTime; + Long createdBy; + LocalDateTime updateTime; + Long updatedBy; + Long version; + + public AccountPO() { + } + + public AccountPO(Long id, String username, String accountStatus, LocalDateTime createTime, Long createdBy, + LocalDateTime updateTime, Long updatedBy, Long version) { + this.id = id; + this.username = username; + this.accountStatus = accountStatus; + this.createTime = createTime; + this.createdBy = createdBy; + this.updateTime = updateTime; + this.updatedBy = updatedBy; + this.version = version; + } public Long getId() { return id; @@ -253,25 +282,78 @@ class TestBean { this.username = username; } - public LocalDate getUsageDate() { - return usageDate; + public String getAccountStatus() { + return accountStatus; } - public void setUsageDate(LocalDate usageDate) { - this.usageDate = usageDate; + public void setAccountStatus(String accountStatus) { + this.accountStatus = accountStatus; } - public Long getUsageDuration() { - return usageDuration; + public LocalDateTime getCreateTime() { + return createTime; } - public void setUsageDuration(Long usageDuration) { - this.usageDuration = usageDuration; + public void setCreateTime(LocalDateTime createTime) { + this.createTime = createTime; + } + + public Long getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(Long createdBy) { + this.createdBy = createdBy; + } + + public LocalDateTime getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(LocalDateTime updateTime) { + this.updateTime = updateTime; + } + + public Long getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(Long updatedBy) { + this.updatedBy = updatedBy; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + @Override + public int hashCode() { + return Objects.hash(id, username, accountStatus, createTime, createdBy, updateTime, updatedBy, version); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AccountPO other = (AccountPO) obj; + return Objects.equals(id, other.id) && Objects.equals(username, other.username) + && Objects.equals(accountStatus, other.accountStatus) && Objects.equals(createTime, other.createTime) + && Objects.equals(createdBy, other.createdBy) && Objects.equals(updateTime, other.updateTime) + && Objects.equals(updatedBy, other.updatedBy) && Objects.equals(version, other.version); } @Override public String toString() { - return "TestBean [id=" + id + ", username=" + username + ", usageDate=" + usageDate + ", usageDuration=" - + usageDuration + "]"; + return "AccountPO [id=" + id + ", username=" + username + ", accountStatus=" + accountStatus + ", createTime=" + + createTime + ", createdBy=" + createdBy + ", updateTime=" + updateTime + ", updatedBy=" + updatedBy + + ", version=" + version + "]"; } } From a1b6492355139457e27b98a4d0a3d2c05bce007e Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 2 Nov 2024 00:22:26 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=B8=AD=E6=BC=94=E7=A4=BA=E5=A6=82=E4=BD=95=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=20id=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java b/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java index 1658586..aa1a1a9 100644 --- a/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java +++ b/src/test/java/xyz/zhouxy/jdbc/test/SimpleJdbcTemplateTests.java @@ -122,6 +122,12 @@ class SimpleJdbcTemplateTests { assertTrue(key.containsKey("create_time")); assertInstanceOf(Date.class, key.get("create_time")); } + List ids = jdbcTemplate.update( + "INSERT INTO sys_account(username, account_status, created_by) VALUES (?, ?, ?), (?, ?, ?)", + buildParams("zhouxy21", "2", 123L, "code22", '2', 456L), + (rs, rowNumber) -> rs.getObject("id", Long.class)); + log.info("ids: {}", ids); + assertEquals(2, ids.size()); } @Test From bd08be59288a3a66db452bf12d7219a637063fc6 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 2 Nov 2024 02:09:53 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20.editorconfig=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c1e2c64 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true From 6b5bcf0b5ce33cb11d3b5388f418113fa421f771 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 2 Nov 2024 10:55:23 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/xyz/zhouxy/jdbc/DbRecord.java | 63 +- .../xyz/zhouxy/jdbc/DefaultBeanRowMapper.java | 43 ++ .../java/xyz/zhouxy/jdbc/ParamBuilder.java | 17 +- .../java/xyz/zhouxy/jdbc/ResultHandler.java | 14 + src/main/java/xyz/zhouxy/jdbc/RowMapper.java | 14 + .../xyz/zhouxy/jdbc/SimpleJdbcTemplate.java | 638 ++++++++++++++++-- 6 files changed, 718 insertions(+), 71 deletions(-) diff --git a/src/main/java/xyz/zhouxy/jdbc/DbRecord.java b/src/main/java/xyz/zhouxy/jdbc/DbRecord.java index 33af73c..16edef9 100644 --- a/src/main/java/xyz/zhouxy/jdbc/DbRecord.java +++ b/src/main/java/xyz/zhouxy/jdbc/DbRecord.java @@ -16,49 +16,78 @@ package xyz.zhouxy.jdbc; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import javax.annotation.Nonnull; + import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; + import xyz.zhouxy.plusone.commons.collection.AbstractMapWrapper; +import xyz.zhouxy.plusone.commons.util.AssertTools; import xyz.zhouxy.plusone.commons.util.OptionalTools; import xyz.zhouxy.plusone.commons.util.StringTools; -import java.util.*; - +/** + * DbRecord + * + *

+ * 封装 Map,表示一条 DB 记录 + *

+ * + * @author ZhouXY + * @since 1.0.0 + */ @Beta public class DbRecord extends AbstractMapWrapper { public DbRecord() { - super(new HashMap<>(), k -> Preconditions.checkArgument(StringTools.isNotBlank(k), "Key must has text."), null); + super(new HashMap<>(), + k -> AssertTools.checkArgument(StringTools.isNotBlank(k), "Key must has text."), + null); } public DbRecord(Map map) { - super(map, k -> Preconditions.checkArgument(StringTools.isNotBlank(k), "Key must has text."), null); + super(map, + k -> AssertTools.checkArgument(StringTools.isNotBlank(k), "Key must has text."), + null); } + /** + * 将值强转为 {@link String},并放在 {@link Optional} 中。 + * 如果 {@code key} 存在,而值不存在,则返回 {@link Optional#empty()}。 + */ public Optional getValueAsString(String key) { return this.getAndConvert(key); } - public List getValueAsList(String key) { - return this.>getAndConvert(key) - .map(l -> (l instanceof List) ? (List) l : new ArrayList<>(l)) - .orElse(Collections.emptyList()); - } - - public Set getValueAsSet(String key) { - return this.>getAndConvert(key) - .map(l -> (l instanceof Set) ? (Set) l : new HashSet<>(l)) - .orElse(Collections.emptySet()); - } - + /** + * 将值强转为 {@code int},并放在 {@link OptionalInt} 中。 + * 如果 {@code key} 存在,而值不存在,则返回 {@link OptionalInt#empty()}。 + */ + @Nonnull public OptionalInt getValueAsInt(String key) { return OptionalTools.toOptionalInt(this.getAndConvert(key)); } + /** + * 将值强转为 {@code long},并放在 {@link OptionalLong} 中。 + * 如果 {@code key} 存在,而值不存在,则返回 {@link OptionalLong#empty()}。 + */ + @Nonnull public OptionalLong getValueAsLong(String key) { return OptionalTools.toOptionalLong(this.getAndConvert(key)); } + /** + * 将值强转为 {@code double},并放在 {@link OptionalDouble} 中。 + * 如果 {@code key} 存在,而值不存在,则返回 {@link OptionalDouble#empty()}。 + */ + @Nonnull public OptionalDouble getValueAsDouble(String key) { return OptionalTools.toOptionalDouble(this.getAndConvert(key)); } diff --git a/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java b/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java index f0dcd6b..901ba84 100644 --- a/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java +++ b/src/main/java/xyz/zhouxy/jdbc/DefaultBeanRowMapper.java @@ -35,9 +35,28 @@ import javax.annotation.Nullable; import com.google.common.base.CaseFormat; +import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod; + +/** + * DefaultBeanRowMapper + * + *

+ * 默认实现的将 {@link ResultSet} 转换为 Java Bean 的 {@link RowMapper}。 + *

+ * + *

+ * NOTE: 使用反射获取类型信息,也是使用反射调用无参构造器和 {@code setter} 方法。 + * 实际使用中还是建议针对目标类型自定义 {@link RowMapper}。 + *

+ * @author ZhouXY + * @since 1.0.0 + */ public class DefaultBeanRowMapper implements RowMapper { + /** Bean 的无参构造器 */ private final Constructor constructor; + + /** 列名到属性的映射 */ private final Map colPropertyMap; private DefaultBeanRowMapper(Constructor constructor, Map colPropertyMap) { @@ -45,10 +64,29 @@ public class DefaultBeanRowMapper implements RowMapper { this.colPropertyMap = colPropertyMap; } + /** + * 创建一个 DefaultBeanRowMapper + * + * @param Bean 类型 + * @param beanType Bean 类型 + * @return DefaultBeanRowMapper 对象 + * @throws SQLException 创建 DefaultBeanRowMapper 出现错误的异常时抛出 + */ + @StaticFactoryMethod(DefaultBeanRowMapper.class) public static DefaultBeanRowMapper of(Class beanType) throws SQLException { return of(beanType, null); } + /** + * 创建一个 DefaultBeanRowMapper + * + * @param Bean 类型 + * @param beanType Bean 类型 + * @param propertyColMap Bean 字段与列名的映射关系。key 是字段,value 是列名。 + * @return DefaultBeanRowMapper 对象 + * @throws SQLException 创建 DefaultBeanRowMapper 出现错误的异常时抛出 + */ + @StaticFactoryMethod(DefaultBeanRowMapper.class) public static DefaultBeanRowMapper of(Class beanType, @Nullable Map propertyColMap) throws SQLException { try { @@ -60,6 +98,7 @@ public class DefaultBeanRowMapper implements RowMapper { BeanInfo beanInfo = Introspector.getBeanInfo(beanType); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + // Bean 的属性名为小驼峰,对应的列名为下划线 Function keyMapper; if (propertyColMap == null || propertyColMap.isEmpty()) { keyMapper = p -> CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, p.getName()); @@ -84,13 +123,17 @@ public class DefaultBeanRowMapper implements RowMapper { } } + /** {@inheritDoc} */ @Override public T mapRow(ResultSet rs, int rowNumber) throws SQLException { try { + // 调用无参构造器创建实例 T newInstance = this.constructor.newInstance(); ResultSetMetaData metaData = rs.getMetaData(); + // 遍历结果的每一列 for (int i = 1; i <= metaData.getColumnCount(); i++) { String colName = metaData.getColumnName(i); + // 获取查询结果列名对应的属性,调用 setter PropertyDescriptor propertyDescriptor = this.colPropertyMap.get(colName); if (propertyDescriptor != null) { Method setter = propertyDescriptor.getWriteMethod(); diff --git a/src/main/java/xyz/zhouxy/jdbc/ParamBuilder.java b/src/main/java/xyz/zhouxy/jdbc/ParamBuilder.java index 3dccdce..5bf9af0 100644 --- a/src/main/java/xyz/zhouxy/jdbc/ParamBuilder.java +++ b/src/main/java/xyz/zhouxy/jdbc/ParamBuilder.java @@ -27,12 +27,21 @@ import java.util.OptionalLong; import java.util.function.Function; import java.util.stream.Collectors; -import com.google.common.base.Preconditions; - import xyz.zhouxy.plusone.commons.collection.CollectionTools; import xyz.zhouxy.plusone.commons.util.ArrayTools; +import xyz.zhouxy.plusone.commons.util.AssertTools; import xyz.zhouxy.plusone.commons.util.OptionalTools; +/** + * ParamBuilder + * + *

+ * JDBC 参数构造器,将数据转换为 {@code Object[]} 类型,以传给 {@link PreparedStatement} + *

+ * + * @author ZhouXY + * @since 1.0.0 + */ public class ParamBuilder { public static final Object[] EMPTY_OBJECT_ARRAY = {}; @@ -60,8 +69,8 @@ public class ParamBuilder { } public static List buildBatchParams(final Collection c, final Function func) { - Preconditions.checkNotNull(c, "The collection can not be null."); - Preconditions.checkNotNull(func, "The func can not be null."); + AssertTools.checkNotNull(c, "The collection can not be null."); + AssertTools.checkNotNull(func, "The func can not be null."); if (CollectionTools.isEmpty(c)) { return Collections.emptyList(); } diff --git a/src/main/java/xyz/zhouxy/jdbc/ResultHandler.java b/src/main/java/xyz/zhouxy/jdbc/ResultHandler.java index b15019e..ff64392 100644 --- a/src/main/java/xyz/zhouxy/jdbc/ResultHandler.java +++ b/src/main/java/xyz/zhouxy/jdbc/ResultHandler.java @@ -19,7 +19,21 @@ package xyz.zhouxy.jdbc; import java.sql.ResultSet; import java.sql.SQLException; +/** + * ResultHandler + * + *

+ * 处理 {@link ResultSet} + *

+ * + * @author ZhouXY + * @since 1.0.0 + */ @FunctionalInterface public interface ResultHandler { + + /** + * 将 {@link ResultSet} 转换为指定类型的对象 + */ T handle(ResultSet resultSet) throws SQLException; } diff --git a/src/main/java/xyz/zhouxy/jdbc/RowMapper.java b/src/main/java/xyz/zhouxy/jdbc/RowMapper.java index 404013f..e7b6b33 100644 --- a/src/main/java/xyz/zhouxy/jdbc/RowMapper.java +++ b/src/main/java/xyz/zhouxy/jdbc/RowMapper.java @@ -22,10 +22,21 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.Map; +/** + * RowMapper + * + *

+ * {@link ResultSet} 中每一行数据的处理逻辑。 + *

+ * + * @author ZhouXY + * @since 1.0.0 + */ @FunctionalInterface public interface RowMapper { T mapRow(ResultSet rs, int rowNumber) throws SQLException; + /** 每一行数据转换为 {@link HashMap} */ public static final RowMapper> HASH_MAP_MAPPER = (rs, rowNumber) -> { Map result = new HashMap<>(); ResultSetMetaData metaData = rs.getMetaData(); @@ -37,13 +48,16 @@ public interface RowMapper { return result; }; + /** 每一行数据转换为 {@link DbRecord} */ public static final RowMapper RECORD_MAPPER = (rs, rowNumber) -> new DbRecord(HASH_MAP_MAPPER.mapRow(rs, rowNumber)); + /** 默认实现的将 {@link ResultSet} 转换为 Java Bean 的 {@link RowMapper}。 */ public static RowMapper beanRowMapper(Class beanType) throws SQLException { return DefaultBeanRowMapper.of(beanType); } + /** 默认实现的将 {@link ResultSet} 转换为 Java Bean 的 {@link RowMapper}。 */ public static RowMapper beanRowMapper(Class beanType, Map propertyColMap) throws SQLException { return DefaultBeanRowMapper.of(beanType, propertyColMap); diff --git a/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java b/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java index fc44d6c..c7beca8 100644 --- a/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java +++ b/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java @@ -39,9 +39,21 @@ import javax.sql.DataSource; import com.google.common.collect.Lists; import xyz.zhouxy.plusone.commons.collection.CollectionTools; +import xyz.zhouxy.plusone.commons.function.ThrowingConsumer; +import xyz.zhouxy.plusone.commons.function.ThrowingPredicate; import xyz.zhouxy.plusone.commons.util.AssertTools; import xyz.zhouxy.plusone.commons.util.OptionalTools; +/** + * SimpleJdbcTemplate + * + *

+ * 对 JDBC 的简单封装,方便数据库操作,支持事务,支持批量操作,支持自定义结果集映射 + *

+ * + * @author ZhouXY + * @since 1.0.0 + */ public class SimpleJdbcTemplate { @Nonnull @@ -54,6 +66,13 @@ public class SimpleJdbcTemplate { // #region - query + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param sql SQL + * @param params 参数 + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ public T query(String sql, Object[] params, ResultHandler resultHandler) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -61,6 +80,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param sql SQL + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ public T query(String sql, ResultHandler resultHandler) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -72,6 +97,13 @@ public class SimpleJdbcTemplate { // #region - queryList + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public List queryList(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -79,6 +111,13 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param sql SQL + * @param params 参数 + * @param clazz 将结果映射为指定的类型 + */ public List queryList(String sql, Object[] params, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -86,6 +125,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,每一行数据映射为 {@code Map},返回结果列表 + * + * @param sql SQL + * @param params 参数列表 + */ public List> queryList(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -93,6 +138,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 + * + * @param sql SQL + * @param params 参数列表 + */ public List queryRecordList(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -100,6 +151,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param sql SQL + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public List queryList(String sql, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -107,6 +164,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param sql SQL + * @param clazz 将结果映射为指定的类型 + */ public List queryList(String sql, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -114,6 +177,11 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,每一行数据映射为 {@code Map},返回结果列表 + * + * @param sql SQL + */ public List> queryList(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -121,6 +189,11 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 + * + * @param sql SQL + */ public List queryRecordList(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -132,6 +205,13 @@ public class SimpleJdbcTemplate { // #region - queryFirst + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public Optional queryFirst(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -139,6 +219,14 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param params 参数 + * @param clazz 目标类型 + */ public Optional queryFirst(String sql, Object[] params, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -146,6 +234,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,将第一行数据转为 Map + * + * @param sql SQL + * @param params 参数 + */ public Optional> queryFirst(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -153,6 +247,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,将第一行数据转为 DbRecord + * + * @param sql SQL + * @param params 参数 + */ public Optional queryFirstRecord(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -160,6 +260,12 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为字符串 + * + * @param sql SQL + * @param params 参数 + */ public Optional queryFirstString(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -167,6 +273,12 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为整数值 + * + * @param sql SQL + * @param params 参数 + */ public OptionalInt queryFirstInt(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -174,6 +286,12 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为长整型 + * + * @param sql SQL + * @param params 参数 + */ public OptionalLong queryFirstLong(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -181,6 +299,12 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param sql SQL + * @param params 参数 + */ public OptionalDouble queryFirstDouble(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -188,6 +312,12 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param sql SQL + * @param params 参数 + */ public Optional queryFirstBigDecimal(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -195,6 +325,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param sql SQL + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public Optional queryFirst(String sql, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -202,6 +338,13 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param clazz 目标类型 + */ public Optional queryFirst(String sql, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -209,6 +352,11 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,将第一行数据转为 Map + * + * @param sql SQL + */ public Optional> queryFirst(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -216,6 +364,11 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,将第一行数据转为 DbRecord + * + * @param sql SQL + */ public Optional queryFirstRecord(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -223,6 +376,11 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为字符串 + * + * @param sql SQL + */ public Optional queryFirstString(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -230,6 +388,11 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为整数值 + * + * @param sql SQL + */ public OptionalInt queryFirstInt(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -237,6 +400,11 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为长整型 + * + * @param sql SQL + */ public OptionalLong queryFirstLong(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -244,6 +412,11 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param sql SQL + */ public OptionalDouble queryFirstDouble(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -251,6 +424,11 @@ public class SimpleJdbcTemplate { } } + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param sql SQL + */ public Optional queryFirstBigDecimal(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -262,6 +440,13 @@ public class SimpleJdbcTemplate { // #region - update & batchUpdate + /** + * 执行更新操作 + * + * @param sql 要执行的 SQL + * @param params 参数 + * @return 更新记录数 + */ public int update(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -269,6 +454,12 @@ public class SimpleJdbcTemplate { } } + /** + * 执行更新操作 + * + * @param sql 要执行的 SQL + * @return 更新记录数 + */ public int update(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -277,13 +468,13 @@ public class SimpleJdbcTemplate { } /** - * 执行 SQL 并更新后的数据 - * - * @param sql 要执行的 SQL 语句 + * 执行 SQL 并返回生成的 keys + * + * @param sql 要执行的 SQL * @param params 参数 - * @param rowMapper 结果映射规则 - * - * @return 更新的数据 + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys * @throws SQLException 执行 SQL 遇到异常情况将抛出 */ public List update(String sql, Object[] params, RowMapper rowMapper) @@ -294,13 +485,12 @@ public class SimpleJdbcTemplate { } /** - * 执行 SQL 并更新后的数据 - * - * @param sql 要执行的 SQL 语句 - * @param params 参数 - * @param rowMapper 结果映射规则 - * - * @return 更新的数据 + * 执行 SQL 并返回生成的 keys + * + * @param sql 要执行的 SQL + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys * @throws SQLException 执行 SQL 遇到异常情况将抛出 */ public List update(String sql, RowMapper rowMapper) @@ -310,6 +500,13 @@ public class SimpleJdbcTemplate { } } + /** + * 执行批量更新,批量更新数据,返回每条记录更新的行数 + * + * @param sql SQL 语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + */ public List batchUpdate(String sql, @Nullable Collection params, int batchSize) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { @@ -317,6 +514,14 @@ public class SimpleJdbcTemplate { } } + /** + * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 + * + * @param sql sql语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + * @param exceptions 异常列表,用于记录异常信息 + */ public List batchUpdateAndIgnoreException(String sql, @Nullable Collection params, int batchSize, List exceptions) throws SQLException { @@ -329,14 +534,26 @@ public class SimpleJdbcTemplate { // #region - transaction - public void executeTransaction(@Nonnull final DbOperations operations) + /** + * 执行事务。如果未发生异常,则提交事务;当有异常发生时,回滚事务 + * + *

+ * operations 中使用 JdbcExecutor 实参进行 JDBC 操作,这些操作在一个连接中 + *

+ * + * @param 异常类型 + * @param operations 事务操作 + * @throws SQLException SQL 异常 + * @throws E 事务中的异常 + */ + public void executeTransaction(@Nonnull final ThrowingConsumer operations) throws SQLException, E { AssertTools.checkNotNull(operations, "Operations can not be null."); try (Connection conn = this.dataSource.getConnection()) { final boolean autoCommit = conn.getAutoCommit(); try { conn.setAutoCommit(false); - operations.execute(new JdbcExecutor(conn)); + operations.accept(new JdbcExecutor(conn)); conn.commit(); } catch (Exception e) { @@ -349,7 +566,17 @@ public class SimpleJdbcTemplate { } } - public void commitIfTrue(@Nonnull final PredicateWithThrowable operations) + /** + * 执行事务。 + * 如果 {@code operations} 返回 {@code true},则提交事务; + * 如果抛出异常,或返回 {@code false},则回滚事务 + * + * @param 事务中的异常 + * @param operations 事务操作 + * @throws SQLException 数据库异常 + * @throws E 事务中的异常类型 + */ + public void commitIfTrue(@Nonnull final ThrowingPredicate operations) throws SQLException, E { AssertTools.checkNotNull(operations, "Operations can not be null."); try (Connection conn = this.dataSource.getConnection()) { @@ -373,16 +600,6 @@ public class SimpleJdbcTemplate { } } - @FunctionalInterface - public interface DbOperations { - void execute(JdbcExecutor jdbcExecutor) throws E; - } - - @FunctionalInterface - public interface PredicateWithThrowable { - boolean test(JdbcExecutor jdbcExecutor) throws E; - } - // #endregion public static final class JdbcExecutor { @@ -395,55 +612,116 @@ public class SimpleJdbcTemplate { // #region - query - public T query(String sql, Object[] params, ResultHandler resulthHandler) + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param sql SQL + * @param params 参数 + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ + public T query(String sql, Object[] params, ResultHandler resultHandler) throws SQLException { - return JdbcExecutor.query(this.conn, sql, params, resulthHandler); + return JdbcExecutor.query(this.conn, sql, params, resultHandler); } - public T query(String sql, ResultHandler resulthHandler) + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param sql SQL + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ + public T query(String sql, ResultHandler resultHandler) throws SQLException { - return JdbcExecutor.query(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resulthHandler); + return JdbcExecutor.query(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler); } // #endregion // #region - queryList + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public List queryList(String sql, Object[] params, RowMapper rowMapper) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, params, rowMapper); } + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param sql SQL + * @param params 参数 + * @param clazz 将结果映射为指定的类型 + */ public List queryList(String sql, Object[] params, Class clazz) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, params, clazz); } + /** + * 执行查询,每一行数据映射为 {@code Map},返回结果列表 + * + * @param sql SQL + * @param params 参数列表 + */ public List> queryList(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); } + /** + * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 + * + * @param sql SQL + * @param params 参数列表 + */ public List queryRecordList(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, params, RowMapper.RECORD_MAPPER); } + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param sql SQL + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public List queryList(String sql, RowMapper rowMapper) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param sql SQL + * @param clazz 将结果映射为指定的类型 + */ public List queryList(String sql, Class clazz) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } + /** + * 执行查询,每一行数据映射为 {@code Map},返回结果列表 + * + * @param sql SQL + */ public List> queryList(String sql) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); } + /** + * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 + * + * @param sql SQL + */ public List queryRecordList(String sql) throws SQLException { return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); @@ -453,91 +731,196 @@ public class SimpleJdbcTemplate { // #region - queryFirst + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public Optional queryFirst(String sql, Object[] params, RowMapper rowMapper) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, params, rowMapper); } + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param params 参数 + * @param clazz 目标类型 + */ public Optional queryFirst(String sql, Object[] params, Class clazz) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, params, clazz); } + /** + * 执行查询,将第一行数据转为 Map + * + * @param sql SQL + * @param params 参数 + */ public Optional> queryFirst(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); } + /** + * 执行查询,将第一行数据转为 DbRecord + * + * @param sql SQL + * @param params 参数 + */ public Optional queryFirstRecord(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, params, RowMapper.RECORD_MAPPER); } + /** + * 查询第一行第一列,并转换为字符串 + * + * @param sql SQL + * @param params 参数 + */ public Optional queryFirstString(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryFirstString(this.conn, sql, params); } + /** + * 查询第一行第一列,并转换为整数值 + * + * @param sql SQL + * @param params 参数 + */ public OptionalInt queryFirstInt(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryFirstInt(this.conn, sql, params); } + /** + * 查询第一行第一列,并转换为长整型 + * + * @param sql SQL + * @param params 参数 + */ public OptionalLong queryFirstLong(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryFirstLong(this.conn, sql, params); } + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param sql SQL + * @param params 参数 + */ public OptionalDouble queryFirstDouble(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryFirstDouble(this.conn, sql, params); } + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param sql SQL + * @param params 参数 + */ public Optional queryFirstBigDecimal(String sql, Object[] params) throws SQLException { return JdbcExecutor.queryFirstBigDecimal(this.conn, sql, params); } + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param sql SQL + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ public Optional queryFirst(String sql, RowMapper rowMapper) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param clazz 目标类型 + */ public Optional queryFirst(String sql, Class clazz) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } + /** + * 执行查询,将第一行数据转为 Map + * + * @param sql SQL + */ public Optional> queryFirst(String sql) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); } + /** + * 执行查询,将第一行数据转为 DbRecord + * + * @param sql SQL + */ public Optional queryFirstRecord(String sql) throws SQLException { return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); } + /** + * 查询第一行第一列,并转换为字符串 + * + * @param sql SQL + */ public Optional queryFirstString(String sql) throws SQLException { return JdbcExecutor.queryFirstString(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } + /** + * 查询第一行第一列,并转换为整数值 + * + * @param sql SQL + */ public OptionalInt queryFirstInt(String sql) throws SQLException { return JdbcExecutor.queryFirstInt(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } + /** + * 查询第一行第一列,并转换为长整型 + * + * @param sql SQL + */ public OptionalLong queryFirstLong(String sql) throws SQLException { return JdbcExecutor.queryFirstLong(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param sql SQL + */ public OptionalDouble queryFirstDouble(String sql) throws SQLException { return JdbcExecutor.queryFirstDouble(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param sql SQL + */ public Optional queryFirstBigDecimal(String sql) throws SQLException { return JdbcExecutor.queryFirstBigDecimal(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); @@ -547,24 +930,37 @@ public class SimpleJdbcTemplate { // #region - update & batchUpdate + /** + * 执行更新操作 + * + * @param sql 要执行的 SQL + * @param params 参数 + * @return 更新记录数 + */ public int update(String sql, Object[] params) throws SQLException { return JdbcExecutor.update(this.conn, sql, params); } + /** + * 执行更新操作 + * + * @param sql 要执行的 SQL + * @return 更新记录数 + */ public int update(String sql) throws SQLException { return JdbcExecutor.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } /** - * 执行 SQL 并更新后的数据 - * - * @param sql 要执行的 SQL 语句 + * 执行 SQL 并返回生成的 keys + * + * @param sql 要执行的 SQL * @param params 参数 - * @param rowMapper 结果映射规则 - * - * @return 更新的数据 + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys * @throws SQLException 执行 SQL 遇到异常情况将抛出 */ public List update(String sql, Object[] params, RowMapper rowMapper) @@ -573,13 +969,12 @@ public class SimpleJdbcTemplate { } /** - * 执行 SQL 并更新后的数据 - * - * @param sql 要执行的 SQL 语句 - * @param params 参数 - * @param rowMapper 结果映射规则 - * - * @return 更新的数据 + * 执行 SQL 并返回生成的 keys + * + * @param sql 要执行的 SQL + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys * @throws SQLException 执行 SQL 遇到异常情况将抛出 */ public List update(String sql, RowMapper rowMapper) @@ -587,11 +982,26 @@ public class SimpleJdbcTemplate { return JdbcExecutor.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } + /** + * 执行批量更新,批量更新数据,返回每条记录更新的行数 + * + * @param sql SQL 语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + */ public List batchUpdate(String sql, @Nullable Collection params, int batchSize) throws SQLException { return JdbcExecutor.batchUpdate(this.conn, sql, params, batchSize); } + /** + * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 + * + * @param sql sql语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + * @param exceptions 异常列表,用于记录异常信息 + */ public List batchUpdateAndIgnoreException(String sql, @Nullable Collection params, int batchSize, List exceptions) throws SQLException { @@ -602,6 +1012,14 @@ public class SimpleJdbcTemplate { // #region - internal + /** + * 执行查询,将查询结果按照指定逻辑进行处理并返回 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ private static T queryInternal(@Nonnull Connection conn, @Nonnull String sql, @Nullable Object[] params, @@ -615,6 +1033,14 @@ public class SimpleJdbcTemplate { } } + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ private static List queryListInternal(@Nonnull Connection conn, @Nonnull String sql, @Nullable Object[] params, @@ -631,6 +1057,14 @@ public class SimpleJdbcTemplate { }); } + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper 行数据映射逻辑 + */ private static Optional queryFirstInternal(@Nonnull Connection conn, @Nonnull String sql, @Nullable Object[] params, @@ -648,6 +1082,14 @@ public class SimpleJdbcTemplate { // #region - query + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ private static T query(Connection conn, String sql, Object[] params, ResultHandler resultHandler) throws SQLException { assertConnectionNotNull(conn); @@ -660,6 +1102,14 @@ public class SimpleJdbcTemplate { // #region - queryList + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ private static List queryList(Connection conn, String sql, Object[] params, RowMapper rowMapper) throws SQLException { assertConnectionNotNull(conn); @@ -668,6 +1118,14 @@ public class SimpleJdbcTemplate { return queryListInternal(conn, sql, params, rowMapper); } + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param clazz 将结果映射为指定的类型 + */ private static List queryList(Connection conn, String sql, Object[] params, Class clazz) throws SQLException { assertConnectionNotNull(conn); @@ -680,6 +1138,14 @@ public class SimpleJdbcTemplate { // #region - queryFirst + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ private static Optional queryFirst(Connection conn, String sql, Object[] params, RowMapper rowMapper) throws SQLException { assertConnectionNotNull(conn); @@ -688,6 +1154,14 @@ public class SimpleJdbcTemplate { return queryFirstInternal(conn, sql, params, rowMapper); } + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param params 参数 + * @param clazz 目标类型 + */ private static Optional queryFirst(Connection conn, String sql, Object[] params, Class clazz) throws SQLException { assertConnectionNotNull(conn); @@ -696,29 +1170,64 @@ public class SimpleJdbcTemplate { return queryFirstInternal(conn, sql, params, (rs, rowNumber) -> rs.getObject(1, clazz)); } + /** + * 查询第一行第一列,并转换为字符串 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ private static Optional queryFirstString(Connection conn, String sql, Object[] params) throws SQLException { return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getString(1)); } + /** + * 查询第一行第一列,并转换为整数值 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ private static OptionalInt queryFirstInt(Connection conn, String sql, Object[] params) throws SQLException { Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getInt(1)); return OptionalTools.toOptionalInt(result); } + /** + * 查询第一行第一列,并转换为长整型 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ private static OptionalLong queryFirstLong(Connection conn, String sql, Object[] params) throws SQLException { Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getLong(1)); return OptionalTools.toOptionalLong(result); } + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ private static OptionalDouble queryFirstDouble(Connection conn, String sql, Object[] params) throws SQLException { Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getDouble(1)); return OptionalTools.toOptionalDouble(result); } + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ private static Optional queryFirstBigDecimal(Connection conn, String sql, Object[] params) throws SQLException { return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getBigDecimal(1)); @@ -728,6 +1237,14 @@ public class SimpleJdbcTemplate { // #region - update & batchUpdate + /** + * 执行更新操作 + * + * @param conn 数据库连接 + * @param sql 要执行的 SQL + * @param params 参数 + * @return 更新记录数 + */ private static int update(Connection conn, String sql, Object[] params) throws SQLException { assertConnectionNotNull(conn); @@ -739,13 +1256,14 @@ public class SimpleJdbcTemplate { } /** - * 执行 SQL 并更新后的数据 - * - * @param sql 要执行的 SQL 语句 + * 执行 SQL 并返回生成的 keys + * + * @param conn 数据库连接 + * @param sql 要执行的 SQL * @param params 参数 - * @param rowMapper 结果映射规则 - * - * @return 更新的数据 + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys * @throws SQLException 执行 SQL 遇到异常情况将抛出 */ private static List update(Connection conn, String sql, Object[] params, RowMapper rowMapper) @@ -768,6 +1286,14 @@ public class SimpleJdbcTemplate { } } + /** + * 执行批量更新,批量更新数据,返回每条记录更新的行数 + * + * @param conn 数据库连接 + * @param sql SQL 语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + */ private static List batchUpdate(Connection conn, String sql, Collection params, int batchSize) throws SQLException { assertConnectionNotNull(conn); @@ -796,6 +1322,15 @@ public class SimpleJdbcTemplate { } } + /** + * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 + * + * @param conn 数据库连接 + * @param sql sql语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + * @param exceptions 异常列表,用于记录异常信息 + */ private static List batchUpdateAndIgnoreException(Connection conn, String sql, @Nullable Collection params, int batchSize, List exceptions) @@ -837,6 +1372,9 @@ public class SimpleJdbcTemplate { } } + /** + * 填充参数 + */ private static void fillStatement(@Nonnull PreparedStatement stmt, @Nullable Object[] params) throws SQLException { if (params != null && params.length > 0) { @@ -859,7 +1397,7 @@ public class SimpleJdbcTemplate { } } - // #region - Asserts + // #region - 参数校验 private static void assertConnectionNotNull(Connection conn) { AssertTools.checkArgumentNotNull(conn, "The argument \"conn\" could not be null."); From 01fda8427655b811fd3e7b75536b73b9526f88f2 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Sat, 2 Nov 2024 11:33:15 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E5=B0=86=E9=80=9A=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=20JDBC=20=E8=BF=9B=E8=A1=8C=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E7=9A=84=E9=9D=99=E6=80=81=E6=96=B9=E6=B3=95=E5=B0=81=E8=A3=85?= =?UTF-8?q?=E5=88=B0=20JdbcOperationSupport=EF=BC=9B=E4=BD=BF=E7=94=A8=20J?= =?UTF-8?q?dbcOperations=20=E5=AE=9A=E4=B9=89=20SimpleJdbcTemplate=20?= =?UTF-8?q?=E5=92=8C=20JdbcExecutor=20=E7=9A=84=E8=A1=8C=E4=B8=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyz/zhouxy/jdbc/JdbcOperationSupport.java | 471 +++++++ .../java/xyz/zhouxy/jdbc/JdbcOperations.java | 359 ++++++ .../xyz/zhouxy/jdbc/SimpleJdbcTemplate.java | 1122 +++-------------- 3 files changed, 1037 insertions(+), 915 deletions(-) create mode 100644 src/main/java/xyz/zhouxy/jdbc/JdbcOperationSupport.java create mode 100644 src/main/java/xyz/zhouxy/jdbc/JdbcOperations.java diff --git a/src/main/java/xyz/zhouxy/jdbc/JdbcOperationSupport.java b/src/main/java/xyz/zhouxy/jdbc/JdbcOperationSupport.java new file mode 100644 index 0000000..b1ae1bd --- /dev/null +++ b/src/main/java/xyz/zhouxy/jdbc/JdbcOperationSupport.java @@ -0,0 +1,471 @@ +/* + * Copyright 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.zhouxy.jdbc; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.common.collect.Lists; + +import xyz.zhouxy.plusone.commons.collection.CollectionTools; +import xyz.zhouxy.plusone.commons.util.AssertTools; +import xyz.zhouxy.plusone.commons.util.OptionalTools; + +/** + * JdbcOperationSupport + * + *

+ * 提供静态方法,封装 JDBC 基础操作 + *

+ * + * @author ZhouXY + * @since 1.0.0 + */ +class JdbcOperationSupport { + + // #region - query + + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ + static T query(Connection conn, String sql, Object[] params, ResultHandler resultHandler) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + assertResultHandlerNotNull(resultHandler); + return queryInternal(conn, sql, params, resultHandler); + } + + // #endregion + + // #region - queryList + + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ + static List queryList(Connection conn, String sql, Object[] params, RowMapper rowMapper) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + assertRowMapperNotNull(rowMapper); + return queryListInternal(conn, sql, params, rowMapper); + } + + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param clazz 将结果映射为指定的类型 + */ + static List queryList(Connection conn, String sql, Object[] params, Class clazz) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + assertClazzNotNull(clazz); + return queryListInternal(conn, sql, params, (rs, rowNumber) -> rs.getObject(1, clazz)); + } + + // #endregion + + // #region - queryFirst + + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ + static Optional queryFirst(Connection conn, String sql, Object[] params, RowMapper rowMapper) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + assertRowMapperNotNull(rowMapper); + return queryFirstInternal(conn, sql, params, rowMapper); + } + + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param params 参数 + * @param clazz 目标类型 + */ + static Optional queryFirst(Connection conn, String sql, Object[] params, Class clazz) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + assertClazzNotNull(clazz); + return queryFirstInternal(conn, sql, params, (rs, rowNumber) -> rs.getObject(1, clazz)); + } + + /** + * 查询第一行第一列,并转换为字符串 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ + static Optional queryFirstString(Connection conn, String sql, Object[] params) + throws SQLException { + return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getString(1)); + } + + /** + * 查询第一行第一列,并转换为整数值 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ + static OptionalInt queryFirstInt(Connection conn, String sql, Object[] params) + throws SQLException { + Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getInt(1)); + return OptionalTools.toOptionalInt(result); + } + + /** + * 查询第一行第一列,并转换为长整型 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ + static OptionalLong queryFirstLong(Connection conn, String sql, Object[] params) + throws SQLException { + Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getLong(1)); + return OptionalTools.toOptionalLong(result); + } + + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ + static OptionalDouble queryFirstDouble(Connection conn, String sql, Object[] params) + throws SQLException { + Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getDouble(1)); + return OptionalTools.toOptionalDouble(result); + } + + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + */ + static Optional queryFirstBigDecimal(Connection conn, String sql, Object[] params) + throws SQLException { + return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getBigDecimal(1)); + } + + // #endregion + + // #region - update & batchUpdate + + /** + * 执行更新操作 + * + * @param conn 数据库连接 + * @param sql 要执行的 SQL + * @param params 参数 + * @return 更新记录数 + */ + static int update(Connection conn, String sql, Object[] params) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + fillStatement(stmt, params); + return stmt.executeUpdate(); + } + } + + /** + * 执行 SQL 并返回生成的 keys + * + * @param conn 数据库连接 + * @param sql 要执行的 SQL + * @param params 参数 + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys + * @throws SQLException 执行 SQL 遇到异常情况将抛出 + */ + static List update(Connection conn, String sql, Object[] params, RowMapper rowMapper) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + assertRowMapperNotNull(rowMapper); + final List result = new ArrayList<>(); + try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { + fillStatement(stmt, params); + stmt.executeUpdate(); + try (ResultSet generatedKeys = stmt.getGeneratedKeys();) { + int rowNumber = 0; + while (generatedKeys.next()) { + T e = rowMapper.mapRow(generatedKeys, rowNumber++); + result.add(e); + } + } + return result; + } + } + + /** + * 执行批量更新,批量更新数据,返回每条记录更新的行数 + * + * @param conn 数据库连接 + * @param sql SQL 语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + */ + static List batchUpdate(Connection conn, String sql, Collection params, int batchSize) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + + if (params == null || params.isEmpty()) { + return Collections.emptyList(); + } + int executeCount = params.size() / batchSize; + executeCount = (params.size() % batchSize == 0) ? executeCount : (executeCount + 1); + List result = Lists.newArrayListWithCapacity(executeCount); + + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + int i = 0; + for (Object[] ps : params) { + i++; + fillStatement(stmt, ps); + stmt.addBatch(); + if (i % batchSize == 0 || i >= params.size()) { + int[] n = stmt.executeBatch(); + result.add(n); + stmt.clearBatch(); + } + } + return result; + } + } + + /** + * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 + * + * @param conn 数据库连接 + * @param sql sql语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + * @param exceptions 异常列表,用于记录异常信息 + */ + static List batchUpdateAndIgnoreException(Connection conn, + String sql, @Nullable Collection params, int batchSize, + List exceptions) + throws SQLException { + assertConnectionNotNull(conn); + assertSqlNotNull(sql); + AssertTools.checkArgument(CollectionTools.isNotEmpty(exceptions), + "The list used to store exceptions should be non-null and empty."); + if (params == null || params.isEmpty()) { + return Collections.emptyList(); + } + int executeCount = params.size() / batchSize; + executeCount = (params.size() % batchSize == 0) ? executeCount : (executeCount + 1); + List result = Lists.newArrayListWithCapacity(executeCount); + + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + int i = 0; + for (Object[] ps : params) { + i++; + fillStatement(stmt, ps); + stmt.addBatch(); + final int batchIndex = i % batchSize; + if (batchIndex == 0 || i >= params.size()) { + try { + int[] n = stmt.executeBatch(); + result.add(n); + stmt.clearBatch(); + } + catch (Exception e) { + int n = (i >= params.size() && batchIndex != 0) ? batchIndex : batchSize; + result.add(new int[n]); + stmt.clearBatch(); + // 收集异常信息 + exceptions.add(e); + } + } + } + return result; + } + } + + // #endregion + + // #region - internal + + /** + * 执行查询,将查询结果按照指定逻辑进行处理并返回 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ + private static T queryInternal(@Nonnull Connection conn, + @Nonnull String sql, + @Nullable Object[] params, + @Nonnull ResultHandler resultHandler) + throws SQLException { + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + fillStatement(stmt, params); + try (ResultSet rs = stmt.executeQuery()) { + return resultHandler.handle(rs); + } + } + } + + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ + private static List queryListInternal(@Nonnull Connection conn, + @Nonnull String sql, + @Nullable Object[] params, + @Nonnull RowMapper rowMapper) + throws SQLException { + return queryInternal(conn, sql, params, rs -> { + List result = new ArrayList<>(); + int rowNumber = 0; + while (rs.next()) { + T e = rowMapper.mapRow(rs, rowNumber++); + result.add(e); + } + return result; + }); + } + + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param conn 数据库连接 + * @param sql SQL + * @param params 参数 + * @param rowMapper 行数据映射逻辑 + */ + private static Optional queryFirstInternal(@Nonnull Connection conn, + @Nonnull String sql, + @Nullable Object[] params, + @Nonnull RowMapper rowMapper) + throws SQLException { + return queryInternal(conn, sql, params, rs -> { + if (rs.next()) { + return Optional.ofNullable(rowMapper.mapRow(rs, 0)); + } + return Optional.empty(); + }); + } + + // #endregion + + /** + * 填充参数 + */ + private static void fillStatement(@Nonnull PreparedStatement stmt, @Nullable Object[] params) + throws SQLException { + if (params != null && params.length > 0) { + Object param; + for (int i = 0; i < params.length; i++) { + param = params[i]; + if (param instanceof java.sql.Date) { + stmt.setDate(i + 1, (java.sql.Date) param); + } + else if (param instanceof java.sql.Time) { + stmt.setTime(i + 1, (java.sql.Time) param); + } + else if (param instanceof java.sql.Timestamp) { + stmt.setTimestamp(i + 1, (java.sql.Timestamp) param); + } + else { + stmt.setObject(i + 1, param); + } + } + } + } + + // #region - 参数校验 + + private static void assertConnectionNotNull(Connection conn) { + AssertTools.checkArgumentNotNull(conn, "The argument \"conn\" could not be null."); + } + + private static void assertSqlNotNull(String sql) { + AssertTools.checkArgumentNotNull(sql, "The argument \"sql\" could not be null."); + } + + private static void assertRowMapperNotNull(RowMapper rowMapper) { + AssertTools.checkArgumentNotNull(rowMapper, "The argument \"rowMapper\" could not be null."); + } + + private static void assertResultHandlerNotNull(ResultHandler resultHandler) { + AssertTools.checkArgumentNotNull(resultHandler, "The argument \"resultHandler\" could not be null."); + } + + private static void assertClazzNotNull(Class clazz) { + AssertTools.checkArgumentNotNull(clazz, "The argument \"clazz\" could not be null."); + } + + // #endregion + + private JdbcOperationSupport() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/src/main/java/xyz/zhouxy/jdbc/JdbcOperations.java b/src/main/java/xyz/zhouxy/jdbc/JdbcOperations.java new file mode 100644 index 0000000..2cfcd97 --- /dev/null +++ b/src/main/java/xyz/zhouxy/jdbc/JdbcOperations.java @@ -0,0 +1,359 @@ +package xyz.zhouxy.jdbc; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import javax.annotation.Nullable; + +/** + * JdbcOperations + * + *

+ * 定义 JdbcTemplate 的 API + *

+ * + * @author ZhouXY + * @since 1.0.0 + */ +interface JdbcOperations { + + // #region - query + + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param sql SQL + * @param params 参数 + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ + T query(String sql, Object[] params, ResultHandler resultHandler) + throws SQLException; + + /** + * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 + * + * @param sql SQL + * @param resultHandler 结果处理器,用于处理 {@link ResultSet} + */ + T query(String sql, ResultHandler resultHandler) + throws SQLException; + + // #endregion + + // #region - queryList + + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ + List queryList(String sql, Object[] params, RowMapper rowMapper) + throws SQLException; + + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param sql SQL + * @param params 参数 + * @param clazz 将结果映射为指定的类型 + */ + List queryList(String sql, Object[] params, Class clazz) + throws SQLException; + + /** + * 执行查询,每一行数据映射为 {@code Map},返回结果列表 + * + * @param sql SQL + * @param params 参数列表 + */ + List> queryList(String sql, Object[] params) + throws SQLException; + + /** + * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 + * + * @param sql SQL + * @param params 参数列表 + */ + List queryRecordList(String sql, Object[] params) + throws SQLException; + + /** + * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 + * + * @param sql SQL + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ + List queryList(String sql, RowMapper rowMapper) + throws SQLException; + + /** + * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 + * + * @param sql SQL + * @param clazz 将结果映射为指定的类型 + */ + List queryList(String sql, Class clazz) + throws SQLException; + + /** + * 执行查询,每一行数据映射为 {@code Map},返回结果列表 + * + * @param sql SQL + */ + List> queryList(String sql) + throws SQLException; + + /** + * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 + * + * @param sql SQL + */ + List queryRecordList(String sql) + throws SQLException; + + // #endregion + + // #region - queryFirst + + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param sql SQL + * @param params 参数 + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ + Optional queryFirst(String sql, Object[] params, RowMapper rowMapper) + throws SQLException; + + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param params 参数 + * @param clazz 目标类型 + */ + Optional queryFirst(String sql, Object[] params, Class clazz) + throws SQLException; + + /** + * 执行查询,将第一行数据转为 Map + * + * @param sql SQL + * @param params 参数 + */ + Optional> queryFirst(String sql, Object[] params) + throws SQLException; + + /** + * 执行查询,将第一行数据转为 DbRecord + * + * @param sql SQL + * @param params 参数 + */ + Optional queryFirstRecord(String sql, Object[] params) + throws SQLException; + + /** + * 查询第一行第一列,并转换为字符串 + * + * @param sql SQL + * @param params 参数 + */ + Optional queryFirstString(String sql, Object[] params) + throws SQLException; + + /** + * 查询第一行第一列,并转换为整数值 + * + * @param sql SQL + * @param params 参数 + */ + OptionalInt queryFirstInt(String sql, Object[] params) + throws SQLException; + + /** + * 查询第一行第一列,并转换为长整型 + * + * @param sql SQL + * @param params 参数 + */ + OptionalLong queryFirstLong(String sql, Object[] params) + throws SQLException; + + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param sql SQL + * @param params 参数 + */ + OptionalDouble queryFirstDouble(String sql, Object[] params) + throws SQLException; + + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param sql SQL + * @param params 参数 + */ + Optional queryFirstBigDecimal(String sql, Object[] params) + throws SQLException; + + /** + * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} + * + * @param sql SQL + * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 + */ + Optional queryFirst(String sql, RowMapper rowMapper) + throws SQLException; + + /** + * 查询第一行第一列,并转换为指定类型 + * + * @param 目标类型 + * @param sql SQL + * @param clazz 目标类型 + */ + Optional queryFirst(String sql, Class clazz) + throws SQLException; + + /** + * 执行查询,将第一行数据转为 Map + * + * @param sql SQL + */ + Optional> queryFirst(String sql) + throws SQLException; + + /** + * 执行查询,将第一行数据转为 DbRecord + * + * @param sql SQL + */ + Optional queryFirstRecord(String sql) + throws SQLException; + + /** + * 查询第一行第一列,并转换为字符串 + * + * @param sql SQL + */ + Optional queryFirstString(String sql) + throws SQLException; + + /** + * 查询第一行第一列,并转换为整数值 + * + * @param sql SQL + */ + OptionalInt queryFirstInt(String sql) + throws SQLException; + + /** + * 查询第一行第一列,并转换为长整型 + * + * @param sql SQL + */ + OptionalLong queryFirstLong(String sql) + throws SQLException; + + /** + * 查询第一行第一列,并转换为双精度浮点型 + * + * @param sql SQL + */ + OptionalDouble queryFirstDouble(String sql) + throws SQLException; + + /** + * 查询第一行第一列,并转换为 {@link BigDecimal} + * + * @param sql SQL + */ + Optional queryFirstBigDecimal(String sql) + throws SQLException; + + // #endregion + + // #region - update & batchUpdate + + /** + * 执行更新操作 + * + * @param sql 要执行的 SQL + * @param params 参数 + * @return 更新记录数 + */ + int update(String sql, Object[] params) + throws SQLException; + + /** + * 执行更新操作 + * + * @param sql 要执行的 SQL + * @return 更新记录数 + */ + int update(String sql) + throws SQLException; + + /** + * 执行 SQL 并返回生成的 keys + * + * @param sql 要执行的 SQL + * @param params 参数 + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys + * @throws SQLException 执行 SQL 遇到异常情况将抛出 + */ + List update(String sql, Object[] params, RowMapper rowMapper) + throws SQLException; + + /** + * 执行 SQL 并返回生成的 keys + * + * @param sql 要执行的 SQL + * @param rowMapper 行数据映射逻辑 + * + * @return generated keys + * @throws SQLException 执行 SQL 遇到异常情况将抛出 + */ + List update(String sql, RowMapper rowMapper) + throws SQLException; + + /** + * 执行批量更新,批量更新数据,返回每条记录更新的行数 + * + * @param sql SQL 语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + */ + List batchUpdate(String sql, @Nullable Collection params, int batchSize) + throws SQLException; + + /** + * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 + * + * @param sql sql语句 + * @param params 参数列表 + * @param batchSize 每次批量更新的数据量 + * @param exceptions 异常列表,用于记录异常信息 + */ + List batchUpdateAndIgnoreException(String sql, @Nullable Collection params, + int batchSize, List exceptions) + throws SQLException; + + // #endregion +} diff --git a/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java b/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java index c7beca8..b8a647b 100644 --- a/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java +++ b/src/main/java/xyz/zhouxy/jdbc/SimpleJdbcTemplate.java @@ -18,13 +18,8 @@ package xyz.zhouxy.jdbc; import java.math.BigDecimal; import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -36,13 +31,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.sql.DataSource; -import com.google.common.collect.Lists; - -import xyz.zhouxy.plusone.commons.collection.CollectionTools; import xyz.zhouxy.plusone.commons.function.ThrowingConsumer; import xyz.zhouxy.plusone.commons.function.ThrowingPredicate; import xyz.zhouxy.plusone.commons.util.AssertTools; -import xyz.zhouxy.plusone.commons.util.OptionalTools; /** * SimpleJdbcTemplate @@ -54,7 +45,7 @@ import xyz.zhouxy.plusone.commons.util.OptionalTools; * @author ZhouXY * @since 1.0.0 */ -public class SimpleJdbcTemplate { +public class SimpleJdbcTemplate implements JdbcOperations { @Nonnull private final DataSource dataSource; @@ -66,30 +57,21 @@ public class SimpleJdbcTemplate { // #region - query - /** - * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 - * - * @param sql SQL - * @param params 参数 - * @param resultHandler 结果处理器,用于处理 {@link ResultSet} - */ + /** {@inheritDoc} */ + @Override public T query(String sql, Object[] params, ResultHandler resultHandler) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.query(conn, sql, params, resultHandler); + return JdbcOperationSupport.query(conn, sql, params, resultHandler); } } - /** - * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 - * - * @param sql SQL - * @param resultHandler 结果处理器,用于处理 {@link ResultSet} - */ + /** {@inheritDoc} */ + @Override public T query(String sql, ResultHandler resultHandler) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.query(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler); + return JdbcOperationSupport.query(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler); } } @@ -97,107 +79,75 @@ public class SimpleJdbcTemplate { // #region - queryList - /** - * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 - * - * @param sql SQL - * @param params 参数 - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, params, rowMapper); + return JdbcOperationSupport.queryList(conn, sql, params, rowMapper); } } - /** - * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 - * - * @param sql SQL - * @param params 参数 - * @param clazz 将结果映射为指定的类型 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, Object[] params, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, params, clazz); + return JdbcOperationSupport.queryList(conn, sql, params, clazz); } } - /** - * 执行查询,每一行数据映射为 {@code Map},返回结果列表 - * - * @param sql SQL - * @param params 参数列表 - */ + /** {@inheritDoc} */ + @Override public List> queryList(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, params, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryList(conn, sql, params, RowMapper.HASH_MAP_MAPPER); } } - /** - * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 - * - * @param sql SQL - * @param params 参数列表 - */ + /** {@inheritDoc} */ + @Override public List queryRecordList(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, params, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryList(conn, sql, params, RowMapper.RECORD_MAPPER); } } - /** - * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 - * - * @param sql SQL - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); + return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } } - /** - * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 - * - * @param sql SQL - * @param clazz 将结果映射为指定的类型 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); + return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } } - /** - * 执行查询,每一行数据映射为 {@code Map},返回结果列表 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public List> queryList(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); } } - /** - * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public List queryRecordList(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); } } @@ -205,234 +155,165 @@ public class SimpleJdbcTemplate { // #region - queryFirst - /** - * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} - * - * @param sql SQL - * @param params 参数 - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, params, rowMapper); + return JdbcOperationSupport.queryFirst(conn, sql, params, rowMapper); } } - /** - * 查询第一行第一列,并转换为指定类型 - * - * @param 目标类型 - * @param sql SQL - * @param params 参数 - * @param clazz 目标类型 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, Object[] params, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, params, clazz); + return JdbcOperationSupport.queryFirst(conn, sql, params, clazz); } } - /** - * 执行查询,将第一行数据转为 Map - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional> queryFirst(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, params, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryFirst(conn, sql, params, RowMapper.HASH_MAP_MAPPER); } } - /** - * 执行查询,将第一行数据转为 DbRecord - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstRecord(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, params, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryFirst(conn, sql, params, RowMapper.RECORD_MAPPER); } } - /** - * 查询第一行第一列,并转换为字符串 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstString(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstString(conn, sql, params); + return JdbcOperationSupport.queryFirstString(conn, sql, params); } } - /** - * 查询第一行第一列,并转换为整数值 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public OptionalInt queryFirstInt(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstInt(conn, sql, params); + return JdbcOperationSupport.queryFirstInt(conn, sql, params); } } - /** - * 查询第一行第一列,并转换为长整型 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public OptionalLong queryFirstLong(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstLong(conn, sql, params); + return JdbcOperationSupport.queryFirstLong(conn, sql, params); } } - /** - * 查询第一行第一列,并转换为双精度浮点型 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public OptionalDouble queryFirstDouble(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstDouble(conn, sql, params); + return JdbcOperationSupport.queryFirstDouble(conn, sql, params); } } - /** - * 查询第一行第一列,并转换为 {@link BigDecimal} - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstBigDecimal(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstBigDecimal(conn, sql, params); + return JdbcOperationSupport.queryFirstBigDecimal(conn, sql, params); } } - /** - * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} - * - * @param sql SQL - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); + return JdbcOperationSupport.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } } - /** - * 查询第一行第一列,并转换为指定类型 - * - * @param 目标类型 - * @param sql SQL - * @param clazz 目标类型 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); + return JdbcOperationSupport.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } } - /** - * 执行查询,将第一行数据转为 Map - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional> queryFirst(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); } } - /** - * 执行查询,将第一行数据转为 DbRecord - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstRecord(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); } } - /** - * 查询第一行第一列,并转换为字符串 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstString(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstString(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstString(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } } - /** - * 查询第一行第一列,并转换为整数值 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public OptionalInt queryFirstInt(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstInt(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstInt(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } } - /** - * 查询第一行第一列,并转换为长整型 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public OptionalLong queryFirstLong(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstLong(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstLong(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } } - /** - * 查询第一行第一列,并转换为双精度浮点型 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public OptionalDouble queryFirstDouble(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstDouble(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstDouble(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } } - /** - * 查询第一行第一列,并转换为 {@link BigDecimal} - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstBigDecimal(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.queryFirstBigDecimal(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstBigDecimal(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } } @@ -440,93 +321,58 @@ public class SimpleJdbcTemplate { // #region - update & batchUpdate - /** - * 执行更新操作 - * - * @param sql 要执行的 SQL - * @param params 参数 - * @return 更新记录数 - */ + /** {@inheritDoc} */ + @Override public int update(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.update(conn, sql, params); + return JdbcOperationSupport.update(conn, sql, params); } } - /** - * 执行更新操作 - * - * @param sql 要执行的 SQL - * @return 更新记录数 - */ + /** {@inheritDoc} */ + @Override public int update(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.update(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.update(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } } - /** - * 执行 SQL 并返回生成的 keys - * - * @param sql 要执行的 SQL - * @param params 参数 - * @param rowMapper 行数据映射逻辑 - * - * @return generated keys - * @throws SQLException 执行 SQL 遇到异常情况将抛出 - */ + /** {@inheritDoc} */ + @Override public List update(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.update(conn, sql, params, rowMapper); + return JdbcOperationSupport.update(conn, sql, params, rowMapper); } } - /** - * 执行 SQL 并返回生成的 keys - * - * @param sql 要执行的 SQL - * @param rowMapper 行数据映射逻辑 - * - * @return generated keys - * @throws SQLException 执行 SQL 遇到异常情况将抛出 - */ + /** {@inheritDoc} */ + @Override public List update(String sql, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.update(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); + return JdbcOperationSupport.update(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } } - /** - * 执行批量更新,批量更新数据,返回每条记录更新的行数 - * - * @param sql SQL 语句 - * @param params 参数列表 - * @param batchSize 每次批量更新的数据量 - */ + /** {@inheritDoc} */ + @Override public List batchUpdate(String sql, @Nullable Collection params, int batchSize) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.batchUpdate(conn, sql, params, batchSize); + return JdbcOperationSupport.batchUpdate(conn, sql, params, batchSize); } } - /** - * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 - * - * @param sql sql语句 - * @param params 参数列表 - * @param batchSize 每次批量更新的数据量 - * @param exceptions 异常列表,用于记录异常信息 - */ + /** {@inheritDoc} */ + @Override public List batchUpdateAndIgnoreException(String sql, @Nullable Collection params, int batchSize, List exceptions) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { - return JdbcExecutor.batchUpdateAndIgnoreException(conn, sql, params, batchSize, exceptions); + return JdbcOperationSupport.batchUpdateAndIgnoreException(conn, sql, params, batchSize, exceptions); } } @@ -602,7 +448,7 @@ public class SimpleJdbcTemplate { // #endregion - public static final class JdbcExecutor { + public static final class JdbcExecutor implements JdbcOperations { private final Connection conn; @@ -612,813 +458,259 @@ public class SimpleJdbcTemplate { // #region - query - /** - * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 - * - * @param sql SQL - * @param params 参数 - * @param resultHandler 结果处理器,用于处理 {@link ResultSet} - */ + /** {@inheritDoc} */ + @Override public T query(String sql, Object[] params, ResultHandler resultHandler) throws SQLException { - return JdbcExecutor.query(this.conn, sql, params, resultHandler); + return JdbcOperationSupport.query(this.conn, sql, params, resultHandler); } - /** - * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 - * - * @param sql SQL - * @param resultHandler 结果处理器,用于处理 {@link ResultSet} - */ + /** {@inheritDoc} */ + @Override public T query(String sql, ResultHandler resultHandler) throws SQLException { - return JdbcExecutor.query(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler); + return JdbcOperationSupport.query(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler); } // #endregion // #region - queryList - /** - * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 - * - * @param sql SQL - * @param params 参数 - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, Object[] params, RowMapper rowMapper) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, params, rowMapper); + return JdbcOperationSupport.queryList(this.conn, sql, params, rowMapper); } - /** - * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 - * - * @param sql SQL - * @param params 参数 - * @param clazz 将结果映射为指定的类型 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, Object[] params, Class clazz) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, params, clazz); + return JdbcOperationSupport.queryList(this.conn, sql, params, clazz); } - /** - * 执行查询,每一行数据映射为 {@code Map},返回结果列表 - * - * @param sql SQL - * @param params 参数列表 - */ + /** {@inheritDoc} */ + @Override public List> queryList(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryList(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); } - /** - * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 - * - * @param sql SQL - * @param params 参数列表 - */ + /** {@inheritDoc} */ + @Override public List queryRecordList(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, params, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryList(this.conn, sql, params, RowMapper.RECORD_MAPPER); } - /** - * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 - * - * @param sql SQL - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, RowMapper rowMapper) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); + return JdbcOperationSupport.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } - /** - * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 - * - * @param sql SQL - * @param clazz 将结果映射为指定的类型 - */ + /** {@inheritDoc} */ + @Override public List queryList(String sql, Class clazz) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); + return JdbcOperationSupport.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } - /** - * 执行查询,每一行数据映射为 {@code Map},返回结果列表 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public List> queryList(String sql) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); } - /** - * 执行查询,每一行数据映射为 {@link DbRecord},返回结果列表 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public List queryRecordList(String sql) throws SQLException { - return JdbcExecutor.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); } // #endregion // #region - queryFirst - /** - * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} - * - * @param sql SQL - * @param params 参数 - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, Object[] params, RowMapper rowMapper) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, params, rowMapper); + return JdbcOperationSupport.queryFirst(this.conn, sql, params, rowMapper); } - /** - * 查询第一行第一列,并转换为指定类型 - * - * @param 目标类型 - * @param sql SQL - * @param params 参数 - * @param clazz 目标类型 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, Object[] params, Class clazz) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, params, clazz); + return JdbcOperationSupport.queryFirst(this.conn, sql, params, clazz); } - /** - * 执行查询,将第一行数据转为 Map - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional> queryFirst(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryFirst(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); } - /** - * 执行查询,将第一行数据转为 DbRecord - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstRecord(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, params, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryFirst(this.conn, sql, params, RowMapper.RECORD_MAPPER); } - /** - * 查询第一行第一列,并转换为字符串 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstString(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryFirstString(this.conn, sql, params); + return JdbcOperationSupport.queryFirstString(this.conn, sql, params); } - /** - * 查询第一行第一列,并转换为整数值 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public OptionalInt queryFirstInt(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryFirstInt(this.conn, sql, params); + return JdbcOperationSupport.queryFirstInt(this.conn, sql, params); } - /** - * 查询第一行第一列,并转换为长整型 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public OptionalLong queryFirstLong(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryFirstLong(this.conn, sql, params); + return JdbcOperationSupport.queryFirstLong(this.conn, sql, params); } - /** - * 查询第一行第一列,并转换为双精度浮点型 - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public OptionalDouble queryFirstDouble(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryFirstDouble(this.conn, sql, params); + return JdbcOperationSupport.queryFirstDouble(this.conn, sql, params); } - /** - * 查询第一行第一列,并转换为 {@link BigDecimal} - * - * @param sql SQL - * @param params 参数 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstBigDecimal(String sql, Object[] params) throws SQLException { - return JdbcExecutor.queryFirstBigDecimal(this.conn, sql, params); + return JdbcOperationSupport.queryFirstBigDecimal(this.conn, sql, params); } - /** - * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} - * - * @param sql SQL - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, RowMapper rowMapper) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); + return JdbcOperationSupport.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } - /** - * 查询第一行第一列,并转换为指定类型 - * - * @param 目标类型 - * @param sql SQL - * @param clazz 目标类型 - */ + /** {@inheritDoc} */ + @Override public Optional queryFirst(String sql, Class clazz) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); + return JdbcOperationSupport.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } - /** - * 执行查询,将第一行数据转为 Map - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional> queryFirst(String sql) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); + return JdbcOperationSupport.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); } - /** - * 执行查询,将第一行数据转为 DbRecord - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstRecord(String sql) throws SQLException { - return JdbcExecutor.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); + return JdbcOperationSupport.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.RECORD_MAPPER); } - /** - * 查询第一行第一列,并转换为字符串 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstString(String sql) throws SQLException { - return JdbcExecutor.queryFirstString(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstString(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } - /** - * 查询第一行第一列,并转换为整数值 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public OptionalInt queryFirstInt(String sql) throws SQLException { - return JdbcExecutor.queryFirstInt(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstInt(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } - /** - * 查询第一行第一列,并转换为长整型 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public OptionalLong queryFirstLong(String sql) throws SQLException { - return JdbcExecutor.queryFirstLong(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstLong(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } - /** - * 查询第一行第一列,并转换为双精度浮点型 - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public OptionalDouble queryFirstDouble(String sql) throws SQLException { - return JdbcExecutor.queryFirstDouble(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstDouble(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } - /** - * 查询第一行第一列,并转换为 {@link BigDecimal} - * - * @param sql SQL - */ + /** {@inheritDoc} */ + @Override public Optional queryFirstBigDecimal(String sql) throws SQLException { - return JdbcExecutor.queryFirstBigDecimal(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.queryFirstBigDecimal(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } // #endregion // #region - update & batchUpdate - /** - * 执行更新操作 - * - * @param sql 要执行的 SQL - * @param params 参数 - * @return 更新记录数 - */ + /** {@inheritDoc} */ + @Override public int update(String sql, Object[] params) throws SQLException { - return JdbcExecutor.update(this.conn, sql, params); + return JdbcOperationSupport.update(this.conn, sql, params); } - /** - * 执行更新操作 - * - * @param sql 要执行的 SQL - * @return 更新记录数 - */ + /** {@inheritDoc} */ + @Override public int update(String sql) throws SQLException { - return JdbcExecutor.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); + return JdbcOperationSupport.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); } - /** - * 执行 SQL 并返回生成的 keys - * - * @param sql 要执行的 SQL - * @param params 参数 - * @param rowMapper 行数据映射逻辑 - * - * @return generated keys - * @throws SQLException 执行 SQL 遇到异常情况将抛出 - */ + /** {@inheritDoc} */ + @Override public List update(String sql, Object[] params, RowMapper rowMapper) throws SQLException { - return JdbcExecutor.update(this.conn, sql, params, rowMapper); + return JdbcOperationSupport.update(this.conn, sql, params, rowMapper); } - /** - * 执行 SQL 并返回生成的 keys - * - * @param sql 要执行的 SQL - * @param rowMapper 行数据映射逻辑 - * - * @return generated keys - * @throws SQLException 执行 SQL 遇到异常情况将抛出 - */ + /** {@inheritDoc} */ + @Override public List update(String sql, RowMapper rowMapper) throws SQLException { - return JdbcExecutor.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); + return JdbcOperationSupport.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } - /** - * 执行批量更新,批量更新数据,返回每条记录更新的行数 - * - * @param sql SQL 语句 - * @param params 参数列表 - * @param batchSize 每次批量更新的数据量 - */ + /** {@inheritDoc} */ + @Override public List batchUpdate(String sql, @Nullable Collection params, int batchSize) throws SQLException { - return JdbcExecutor.batchUpdate(this.conn, sql, params, batchSize); + return JdbcOperationSupport.batchUpdate(this.conn, sql, params, batchSize); } - /** - * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 - * - * @param sql sql语句 - * @param params 参数列表 - * @param batchSize 每次批量更新的数据量 - * @param exceptions 异常列表,用于记录异常信息 - */ + /** {@inheritDoc} */ + @Override public List batchUpdateAndIgnoreException(String sql, @Nullable Collection params, int batchSize, List exceptions) throws SQLException { - return JdbcExecutor.batchUpdateAndIgnoreException(this.conn, sql, params, batchSize, exceptions); + return JdbcOperationSupport.batchUpdateAndIgnoreException(this.conn, sql, params, batchSize, exceptions); } // #endregion - // #region - internal - - /** - * 执行查询,将查询结果按照指定逻辑进行处理并返回 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - * @param resultHandler 结果处理器,用于处理 {@link ResultSet} - */ - private static T queryInternal(@Nonnull Connection conn, - @Nonnull String sql, - @Nullable Object[] params, - @Nonnull ResultHandler resultHandler) - throws SQLException { - try (PreparedStatement stmt = conn.prepareStatement(sql)) { - fillStatement(stmt, params); - try (ResultSet rs = stmt.executeQuery()) { - return resultHandler.handle(rs); - } - } - } - - /** - * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ - private static List queryListInternal(@Nonnull Connection conn, - @Nonnull String sql, - @Nullable Object[] params, - @Nonnull RowMapper rowMapper) - throws SQLException { - return queryInternal(conn, sql, params, rs -> { - List result = new ArrayList<>(); - int rowNumber = 0; - while (rs.next()) { - T e = rowMapper.mapRow(rs, rowNumber++); - result.add(e); - } - return result; - }); - } - - /** - * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - * @param rowMapper 行数据映射逻辑 - */ - private static Optional queryFirstInternal(@Nonnull Connection conn, - @Nonnull String sql, - @Nullable Object[] params, - @Nonnull RowMapper rowMapper) - throws SQLException { - return queryInternal(conn, sql, params, rs -> { - if (rs.next()) { - return Optional.ofNullable(rowMapper.mapRow(rs, 0)); - } - return Optional.empty(); - }); - } - - // #endregion - - // #region - query - - /** - * 执行查询,并按照自定义处理逻辑对结果进行处理,将结果转换为指定类型并返回 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - * @param resultHandler 结果处理器,用于处理 {@link ResultSet} - */ - private static T query(Connection conn, String sql, Object[] params, ResultHandler resultHandler) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - assertResultHandlerNotNull(resultHandler); - return queryInternal(conn, sql, params, resultHandler); - } - - // #endregion - - // #region - queryList - - /** - * 执行查询,将查询结果的每一行数据按照指定逻辑进行处理,返回结果列表 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ - private static List queryList(Connection conn, String sql, Object[] params, RowMapper rowMapper) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - assertRowMapperNotNull(rowMapper); - return queryListInternal(conn, sql, params, rowMapper); - } - - /** - * 执行查询,返回结果映射为指定的类型。当结果为单列时使用 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - * @param clazz 将结果映射为指定的类型 - */ - private static List queryList(Connection conn, String sql, Object[] params, Class clazz) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - assertClazzNotNull(clazz); - return queryListInternal(conn, sql, params, (rs, rowNumber) -> rs.getObject(1, clazz)); - } - - // #endregion - - // #region - queryFirst - - /** - * 执行查询,将查询结果的第一行数据按照指定逻辑进行处理,返回 {@link Optional} - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - * @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑 - */ - private static Optional queryFirst(Connection conn, String sql, Object[] params, RowMapper rowMapper) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - assertRowMapperNotNull(rowMapper); - return queryFirstInternal(conn, sql, params, rowMapper); - } - - /** - * 查询第一行第一列,并转换为指定类型 - * - * @param 目标类型 - * @param sql SQL - * @param params 参数 - * @param clazz 目标类型 - */ - private static Optional queryFirst(Connection conn, String sql, Object[] params, Class clazz) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - assertClazzNotNull(clazz); - return queryFirstInternal(conn, sql, params, (rs, rowNumber) -> rs.getObject(1, clazz)); - } - - /** - * 查询第一行第一列,并转换为字符串 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - */ - private static Optional queryFirstString(Connection conn, String sql, Object[] params) - throws SQLException { - return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getString(1)); - } - - /** - * 查询第一行第一列,并转换为整数值 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - */ - private static OptionalInt queryFirstInt(Connection conn, String sql, Object[] params) - throws SQLException { - Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getInt(1)); - return OptionalTools.toOptionalInt(result); - } - - /** - * 查询第一行第一列,并转换为长整型 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - */ - private static OptionalLong queryFirstLong(Connection conn, String sql, Object[] params) - throws SQLException { - Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getLong(1)); - return OptionalTools.toOptionalLong(result); - } - - /** - * 查询第一行第一列,并转换为双精度浮点型 - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - */ - private static OptionalDouble queryFirstDouble(Connection conn, String sql, Object[] params) - throws SQLException { - Optional result = queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getDouble(1)); - return OptionalTools.toOptionalDouble(result); - } - - /** - * 查询第一行第一列,并转换为 {@link BigDecimal} - * - * @param conn 数据库连接 - * @param sql SQL - * @param params 参数 - */ - private static Optional queryFirstBigDecimal(Connection conn, String sql, Object[] params) - throws SQLException { - return queryFirst(conn, sql, params, (rs, rowNumber) -> rs.getBigDecimal(1)); - } - - // #endregion - - // #region - update & batchUpdate - - /** - * 执行更新操作 - * - * @param conn 数据库连接 - * @param sql 要执行的 SQL - * @param params 参数 - * @return 更新记录数 - */ - private static int update(Connection conn, String sql, Object[] params) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - try (PreparedStatement stmt = conn.prepareStatement(sql)) { - fillStatement(stmt, params); - return stmt.executeUpdate(); - } - } - - /** - * 执行 SQL 并返回生成的 keys - * - * @param conn 数据库连接 - * @param sql 要执行的 SQL - * @param params 参数 - * @param rowMapper 行数据映射逻辑 - * - * @return generated keys - * @throws SQLException 执行 SQL 遇到异常情况将抛出 - */ - private static List update(Connection conn, String sql, Object[] params, RowMapper rowMapper) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - assertRowMapperNotNull(rowMapper); - final List result = new ArrayList<>(); - try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { - fillStatement(stmt, params); - stmt.executeUpdate(); - try (ResultSet generatedKeys = stmt.getGeneratedKeys();) { - int rowNumber = 0; - while (generatedKeys.next()) { - T e = rowMapper.mapRow(generatedKeys, rowNumber++); - result.add(e); - } - } - return result; - } - } - - /** - * 执行批量更新,批量更新数据,返回每条记录更新的行数 - * - * @param conn 数据库连接 - * @param sql SQL 语句 - * @param params 参数列表 - * @param batchSize 每次批量更新的数据量 - */ - private static List batchUpdate(Connection conn, String sql, Collection params, int batchSize) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - - if (params == null || params.isEmpty()) { - return Collections.emptyList(); - } - int executeCount = params.size() / batchSize; - executeCount = (params.size() % batchSize == 0) ? executeCount : (executeCount + 1); - List result = Lists.newArrayListWithCapacity(executeCount); - - try (PreparedStatement stmt = conn.prepareStatement(sql)) { - int i = 0; - for (Object[] ps : params) { - i++; - fillStatement(stmt, ps); - stmt.addBatch(); - if (i % batchSize == 0 || i >= params.size()) { - int[] n = stmt.executeBatch(); - result.add(n); - stmt.clearBatch(); - } - } - return result; - } - } - - /** - * 批量更新,返回更新成功的记录行数。发生异常时不中断操作,将异常存入 {@code exceptions} 中 - * - * @param conn 数据库连接 - * @param sql sql语句 - * @param params 参数列表 - * @param batchSize 每次批量更新的数据量 - * @param exceptions 异常列表,用于记录异常信息 - */ - private static List batchUpdateAndIgnoreException(Connection conn, - String sql, @Nullable Collection params, int batchSize, - List exceptions) - throws SQLException { - assertConnectionNotNull(conn); - assertSqlNotNull(sql); - AssertTools.checkArgument(CollectionTools.isNotEmpty(exceptions), - "The list used to store exceptions should be non-null and empty."); - if (params == null || params.isEmpty()) { - return Collections.emptyList(); - } - int executeCount = params.size() / batchSize; - executeCount = (params.size() % batchSize == 0) ? executeCount : (executeCount + 1); - List result = Lists.newArrayListWithCapacity(executeCount); - - try (PreparedStatement stmt = conn.prepareStatement(sql)) { - int i = 0; - for (Object[] ps : params) { - i++; - fillStatement(stmt, ps); - stmt.addBatch(); - final int batchIndex = i % batchSize; - if (batchIndex == 0 || i >= params.size()) { - try { - int[] n = stmt.executeBatch(); - result.add(n); - stmt.clearBatch(); - } - catch (Exception e) { - int n = (i >= params.size() && batchIndex != 0) ? batchIndex : batchSize; - result.add(new int[n]); - stmt.clearBatch(); - // 收集异常信息 - exceptions.add(e); - } - } - } - return result; - } - } - - /** - * 填充参数 - */ - private static void fillStatement(@Nonnull PreparedStatement stmt, @Nullable Object[] params) - throws SQLException { - if (params != null && params.length > 0) { - Object param; - for (int i = 0; i < params.length; i++) { - param = params[i]; - if (param instanceof java.sql.Date) { - stmt.setDate(i + 1, (java.sql.Date) param); - } - else if (param instanceof java.sql.Time) { - stmt.setTime(i + 1, (java.sql.Time) param); - } - else if (param instanceof java.sql.Timestamp) { - stmt.setTimestamp(i + 1, (java.sql.Timestamp) param); - } - else { - stmt.setObject(i + 1, param); - } - } - } - } - - // #region - 参数校验 - - private static void assertConnectionNotNull(Connection conn) { - AssertTools.checkArgumentNotNull(conn, "The argument \"conn\" could not be null."); - } - - private static void assertSqlNotNull(String sql) { - AssertTools.checkArgumentNotNull(sql, "The argument \"sql\" could not be null."); - } - - private static void assertRowMapperNotNull(RowMapper rowMapper) { - AssertTools.checkArgumentNotNull(rowMapper, "The argument \"rowMapper\" could not be null."); - } - - private static void assertResultHandlerNotNull(ResultHandler resultHandler) { - AssertTools.checkArgumentNotNull(resultHandler, "The argument \"resultHandler\" could not be null."); - } - - private static void assertClazzNotNull(Class clazz) { - AssertTools.checkArgumentNotNull(clazz, "The argument \"clazz\" could not be null."); - } - - // #endregion } + }