Compare commits
3 Commits
5100fa5f1d
...
7de2a7eec1
| Author | SHA1 | Date | |
|---|---|---|---|
| 7de2a7eec1 | |||
| 76bfff2d90 | |||
| 6f2882cd08 |
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## [1.1.0] - Unreleased
|
||||
|
||||
### ⚠️ 破坏性变更
|
||||
|
||||
- **`DefaultBeanRowMapper.of()` 不再抛出 `SQLException`**:工厂方法在反射异常时改为抛出非受检异常 `IllegalStateException`。调用方如果 `catch (SQLException e)` 包裹 `of()` 调用,该捕获将失效,需移除相关 `catch` 块或改为捕获 `IllegalStateException`。
|
||||
|
||||
### 新增
|
||||
|
||||
- `queryValueOrDefault(sql, params, Class<T>, T defaultValue)`:查询单行单列,结果为空时返回指定默认值
|
||||
- `queryValueOrDefault(sql, Class<T>, T defaultValue)`:无参数重载
|
||||
- 补充 `QueryTest` 中 `queryValueOrDefault` 单元测试 6 个
|
||||
|
||||
### 重构
|
||||
|
||||
**将单列查询方法标记为过时,消除 Class 参数重载歧义**
|
||||
|
||||
- `queryList(sql, params, Class<T>)` → 已过时,请使用 `queryValues(sql, params, Class<T>)`
|
||||
语义明确为"多行单列 → 值列表",不与整行 `RowMapper` 重载混淆
|
||||
- `queryFirst(sql, params, Class<T>)` → 已过时,请使用 `queryValue(sql, params, Class<T>)`
|
||||
语义明确为"单行单列 → 单值",不与整行 `RowMapper` 重载混淆
|
||||
- 同时将对应的无参数重载标记为过时:
|
||||
- `queryList(sql, Class<T>)` → 请使用 `queryValues(sql, Class<T>)`
|
||||
- `queryFirst(sql, Class<T>)` → 请使用 `queryValue(sql, Class<T>)`
|
||||
- 旧方法将在后续版本中移除
|
||||
|
||||
### 文档
|
||||
|
||||
- 优化 `DefaultBeanRowMapper` 类注释,明确性能限制和使用建议
|
||||
|
||||
---
|
||||
|
||||
## [1.0.0] - 2026-06-17
|
||||
|
||||
### 重构
|
||||
|
||||
24
README.md
24
README.md
@@ -16,13 +16,15 @@
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计考量与边界
|
||||
## 2. 设计考量与取舍
|
||||
|
||||
`Simple JDBC` 不追求大而全,在功能设计上保持克制。以下是本项目的一些设计考量:
|
||||
`Simple JDBC` 不追求大而全,在功能设计上保持克制。以下是本项目的一些设计考量与取舍:
|
||||
|
||||
- 明确**不支持存储过程**:专注于基础 CRUD。
|
||||
- **不提供分页 API**:不同数据库的分页方言差异巨大,且实际业务中的分页查询往往不是简单的 `LIMIT OFFSET`(存在如游标分页、延迟关联等深度优化空间)。为了保持轻量与灵活,分页 SQL 交由开发者根据具体数据库与业务场景自行编写。
|
||||
- **不提供缓存支持**:数据缓存应当被视为一个独立的关注点,通常交由更高层的抽象模块来处理。
|
||||
- **不支持直接传入 Connection**:为确保数据库连接资源的规范获取与安全释放,Simple JDBC 舍弃了一定的连接管理灵活性,不支持直接传入 Connection,而是需要通过 `DataSource` 来获取连接(详见[8. 连接池集成](#8-连接池集成))。常规操作由 `SimpleJdbcTemplate` 自动完成连接的获取与归还;事务场景下,由 `TransactionTemplate` 实现连接绑定,保障同一事务内所有操作的连接一致性。
|
||||
- **有限但灵活的结果映射**:为应对多样化的数据处理需求,Simple JDBC 提供了 `ResultHandler` 与 `RowMapper` 两层抽象机制。前者负责整体结果集的统筹处理,后者专注于单行数据的解析与映射(详见 [4.2 结果映射策略](#42-结果映射策略))。两者均设计为函数式接口,支持通过 Lambda 表达式快速定制映射逻辑。
|
||||
|
||||
---
|
||||
|
||||
@@ -76,7 +78,7 @@ List<Account> accounts = jdbcTemplate.query(
|
||||
);
|
||||
|
||||
// 查询列表(单列)
|
||||
List<String> usernames = jdbcTemplate.queryList(
|
||||
List<String> usernames = jdbcTemplate.queryValues(
|
||||
"SELECT username FROM account WHERE deleted = 0 AND username LIKE ? AND org_no = ?",
|
||||
buildParams("admin%", "0000"),
|
||||
String.class
|
||||
@@ -115,11 +117,18 @@ Optional<Account> account = jdbcTemplate.queryFirst(
|
||||
);
|
||||
|
||||
// 查询单个值(所有 ResultSet.getObject 支持的类型)
|
||||
Long count = jdbcTemplate.queryFirst(
|
||||
Long count = jdbcTemplate.queryValue(
|
||||
"SELECT COUNT(*) FROM account WHERE deleted = 0 AND username LIKE ? AND org_no = ?",
|
||||
buildParams("admin%", "0000"),
|
||||
Long.class
|
||||
).orElse(0L);
|
||||
// 或者
|
||||
Long count = jdbcTemplate.queryValueOrDefault(
|
||||
"SELECT COUNT(*) FROM account WHERE deleted = 0 AND username LIKE ? AND org_no = ?",
|
||||
buildParams("admin%", "0000"),
|
||||
Long.class,
|
||||
0L
|
||||
);
|
||||
|
||||
// 查询 Boolean 值
|
||||
boolean exists = jdbcTemplate.queryBoolean(
|
||||
@@ -243,14 +252,15 @@ jdbcTemplate.transaction().commitIfTrue(jdbc -> {
|
||||
| :--- | :--- |
|
||||
| `query(sql, params, resultHandler)` | 最基础的查询,通过 `ResultHandler` 自定义完整的映射逻辑。 |
|
||||
| `queryList(sql, params, rowMapper)` | 查询列表,通过 `RowMapper` 逐行映射。 |
|
||||
| `queryList(sql, params, Class)` | 单列查询列表,每行提取第一列并转换为指定类型。 |
|
||||
| `queryList(sql, params)` | 查询列表,每行自动转换为 `Map<String, Object>`。 |
|
||||
| `queryFirst(sql, params, rowMapper)` | 查询第一行,通过 `RowMapper` 映射,返回 `Optional<T>`。 |
|
||||
| `queryFirst(sql, params, Class)` | 查询第一行第一列,返回 `Optional<T>`。 |
|
||||
| `queryFirst(sql, params)` | 查询第一行,返回 `Optional<Map<String, Object>>`。 |
|
||||
| `queryValues(sql, params, Class)` | 单列查询列表,每行提取第一列并转换为指定类型。 |
|
||||
| `queryValue(sql, params, Class)` | 查询第一行第一列,返回 `Optional<T>`。 |
|
||||
| `queryValueOrDefault(sql, params, Class, default)` | 查询第一行第一列,结果为空时返回默认值。适用于 COUNT/SUM 等聚合查询。 |
|
||||
| `queryBoolean(sql, params)` | 查询第一行第一列并转换为 `boolean`,若结果为空则返回 `false`。 |
|
||||
|
||||
*💡 提示:以上方法均有省略 `params` 的重载(如 `queryList(sql, rowMapper)`),适用于不含占位符的 SQL 语句。*
|
||||
*💡 提示:以上方法均有省略 `params` 的重载(如 `queryList(sql, rowMapper)`),适用于不含占位符的 SQL 语句。`queryValues`、`queryValue`、`queryValueOrDefault` 同理。*
|
||||
|
||||
### 4.2 结果映射策略
|
||||
|
||||
|
||||
@@ -41,16 +41,17 @@ import xyz.zhouxy.jdbc.util.NamingTools;
|
||||
*
|
||||
* <p>
|
||||
* 将 {@link ResultSet} 转换为 Java Bean 的 {@link RowMapper} 的基础实现。
|
||||
* <i>仅在对性能不敏感的场景下使用。</i>
|
||||
* <p>
|
||||
* <i>性能和规则上的限制都比较大,仅在对性能不敏感的场景下便捷使用,
|
||||
* 一般情况下你应该自定义 {@link RowMapper}。</i>
|
||||
*
|
||||
* <p>
|
||||
* 说明:
|
||||
* <ul>
|
||||
* <li>使用反射获取类型信息,也是使用反射调用无参构造器和 {@code setter} 方法。</li>
|
||||
* <li>{@code propertyColMap} 未指定的列名和属性名的映射时,默认 JavaBean 的属性名为小驼峰,列名为小写蛇形命名。</li>
|
||||
* <li>支持自定义列名和属性名的映射,当未指定 {@code propertyColMap} 时,默认 JavaBean 的属性名为小驼峰,列名为小写蛇形命名。</li>
|
||||
* <li>使用 {@link ResultSet#getObject(String, Class)} 从 {@link ResultSet} 中获取属性值。</li>
|
||||
* <li>JavaBean 属性仅支持引用类型,不支持基本数据类型。</li>
|
||||
* <li><b>实际使用中更建议针对目标类型自定义 {@link RowMapper}。</b></li>
|
||||
* </ul>
|
||||
*
|
||||
* @author ZhouXY
|
||||
@@ -86,9 +87,9 @@ public class DefaultBeanRowMapper<T> implements RowMapper<T> {
|
||||
* @param <T> Bean 类型
|
||||
* @param beanType Bean 类型
|
||||
* @return DefaultBeanRowMapper 对象
|
||||
* @throws SQLException 创建 {@code DefaultBeanRowMapper} 出现错误的异常时抛出
|
||||
* @throws IllegalStateException 创建 {@code DefaultBeanRowMapper} 出现错误的异常时抛出
|
||||
*/
|
||||
public static <T> DefaultBeanRowMapper<T> of(Class<T> beanType) throws SQLException {
|
||||
public static <T> DefaultBeanRowMapper<T> of(Class<T> beanType) {
|
||||
return of(beanType, null);
|
||||
}
|
||||
|
||||
@@ -99,10 +100,10 @@ public class DefaultBeanRowMapper<T> implements RowMapper<T> {
|
||||
* @param beanType Bean 类型
|
||||
* @param propertyColMap Bean 字段与列名的映射关系。key 是字段,value 是列名。
|
||||
* @return {@code DefaultBeanRowMapper} 对象
|
||||
* @throws SQLException 创建 {@code DefaultBeanRowMapper} 出现错误的异常时抛出
|
||||
* @throws IllegalStateException 创建 {@code DefaultBeanRowMapper} 出现错误的异常时抛出
|
||||
*/
|
||||
public static <T> DefaultBeanRowMapper<T> of(Class<T> beanType, @Nullable Map<String, String> propertyColMap)
|
||||
throws SQLException {
|
||||
public static <T> DefaultBeanRowMapper<T> of(Class<T> beanType,
|
||||
@Nullable Map<String, String> propertyColMap) {
|
||||
try {
|
||||
// 获取无参构造器
|
||||
Constructor<T> constructor = beanType.getDeclaredConstructor();
|
||||
@@ -113,10 +114,10 @@ public class DefaultBeanRowMapper<T> implements RowMapper<T> {
|
||||
return new DefaultBeanRowMapper<>(beanType, constructor, colPropertyMap, colSetterMap);
|
||||
}
|
||||
catch (IntrospectionException e) {
|
||||
throw new SQLException("There is an exception occurs during introspection.", e);
|
||||
throw new IllegalStateException("There is an exception occurs during introspection.", e);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
throw new SQLException("Could not find a no-args constructor in " + beanType.getName(), e);
|
||||
throw new IllegalStateException("Could not find a no-args constructor in " + beanType.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ public class DefaultBeanRowMapper<T> implements RowMapper<T> {
|
||||
return newInstance;
|
||||
}
|
||||
catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
|
||||
throw new SQLException("Could not map row to " + beanType.getName(), e);
|
||||
throw new IllegalStateException("Could not map row to " + beanType.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,14 +93,14 @@ class JdbcOperationSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询,返回结果映射为指定的类型。当结果为单列时使用
|
||||
* 执行查询,只取结果集每行第一列的值,映射为指定类型并返回列表
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param sql SQL
|
||||
* @param params 参数
|
||||
* @param clazz 将结果映射为指定的类型
|
||||
*/
|
||||
static <T> List<T> queryList(Connection conn, String sql, Object[] params, Class<T> clazz)
|
||||
static <T> List<T> queryValues(Connection conn, String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
assertConnectionNotNull(conn);
|
||||
assertSqlNotNull(sql);
|
||||
@@ -129,14 +129,15 @@ class JdbcOperationSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询第一行第一列,并转换为指定类型
|
||||
* 执行查询,只取结果集第一行第一列的值,映射为指定类型并返回
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param <T> 目标类型
|
||||
* @param sql SQL
|
||||
* @param params 参数
|
||||
* @param clazz 目标类型
|
||||
*/
|
||||
static <T> T queryFirst(Connection conn, String sql, Object[] params, Class<T> clazz)
|
||||
static <T> T queryValue(Connection conn, String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
assertConnectionNotNull(conn);
|
||||
assertSqlNotNull(sql);
|
||||
|
||||
@@ -87,17 +87,18 @@ public interface JdbcOperations {
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* 执行查询,返回结果映射为指定的类型。当结果为单列时使用
|
||||
* 执行查询,只取结果集每行第一列的值,映射为指定类型并返回列表。
|
||||
* 适用于 {@code SELECT single_column FROM ...} 单列查询场景。
|
||||
*
|
||||
* @param <T> 目标类型
|
||||
* @param <T> 目标类型(对应结果集第一列的 Java 类型)
|
||||
* @param sql SQL
|
||||
* @param params 参数
|
||||
* @param clazz 目标类型
|
||||
*
|
||||
* @return 映射结果。如果查询结果为空,则返回空列表
|
||||
* @return 每一行第一列的值列表。如果查询结果为空,则返回空列表
|
||||
* @throws SQLException SQL异常
|
||||
*/
|
||||
<T> List<T> queryList(String sql, Object[] params, Class<T> clazz)
|
||||
<T> List<T> queryValues(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -128,18 +129,39 @@ public interface JdbcOperations {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询,返回结果映射为指定的类型。当结果为单列时使用
|
||||
* 执行查询,只取结果集每行第一列的值,映射为指定类型并返回列表。
|
||||
* 适用于 {@code SELECT single_column FROM ...} 单列查询场景。
|
||||
*
|
||||
* @param <T> 目标类型
|
||||
* @param sql SQL
|
||||
* @param clazz 将结果映射为指定的类型
|
||||
*
|
||||
* @return 查询结果
|
||||
* @return 每一行第一列的值列表。如果查询结果为空,则返回空列表
|
||||
* @throws SQLException SQL 异常
|
||||
*/
|
||||
default <T> List<T> queryValues(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryValues(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 自 1.1.0 起,请使用 {@link #queryValues(String, Object[], Class)}。
|
||||
* 此方法将在后续版本中移除。
|
||||
*/
|
||||
@Deprecated
|
||||
default <T> List<T> queryList(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryValues(sql, params, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 自 1.1.0 起,请使用 {@link #queryValues(String, Class)}。
|
||||
* 此方法将在后续版本中移除。
|
||||
*/
|
||||
@Deprecated
|
||||
default <T> List<T> queryList(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryList(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
return queryValues(sql, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,17 +196,18 @@ public interface JdbcOperations {
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
* 查询第一行第一列,并转换为指定类型
|
||||
* 执行查询,只取结果集第一行第一列的值,映射为指定类型并返回。
|
||||
* 适用于 {@code SELECT single_column FROM ... WHERE ...} 单列单行查询场景。
|
||||
*
|
||||
* @param <T> 目标类型
|
||||
* @param sql SQL
|
||||
* @param params 参数
|
||||
* @param clazz 目标类型
|
||||
*
|
||||
* @return 查询结果
|
||||
* @return 第一行第一列的值。如果查询结果为空,则返回 {@code Optional.empty()}
|
||||
* @throws SQLException SQL 异常
|
||||
*/
|
||||
<T> Optional<T> queryFirst(String sql, Object[] params, Class<T> clazz)
|
||||
<T> Optional<T> queryValue(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
@@ -215,18 +238,39 @@ public interface JdbcOperations {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询第一行第一列,并转换为指定类型
|
||||
* 执行查询,只取结果集第一行第一列的值,映射为指定类型并返回。
|
||||
* 适用于 {@code SELECT single_column FROM ... WHERE ...} 单列单行查询场景。
|
||||
*
|
||||
* @param <T> 目标类型
|
||||
* @param sql SQL
|
||||
* @param clazz 目标类型
|
||||
*
|
||||
* @return 第一行第一列的值,如果查询结果为空,则返回 {@code Optional#empty()}
|
||||
* @return 第一行第一列的值,如果查询结果为空,则返回 {@code Optional.empty()}
|
||||
* @throws SQLException SQL 异常
|
||||
*/
|
||||
default <T> Optional<T> queryValue(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryValue(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 自 1.1.0 起,请使用 {@link #queryValue(String, Object[], Class)}。
|
||||
* 此方法将在后续版本中移除。
|
||||
*/
|
||||
@Deprecated
|
||||
default <T> Optional<T> queryFirst(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryValue(sql, params, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 自 1.1.0 起,请使用 {@link #queryValue(String, Class)}。
|
||||
* 此方法将在后续版本中移除。
|
||||
*/
|
||||
@Deprecated
|
||||
default <T> Optional<T> queryFirst(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryFirst(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
return queryValue(sql, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,6 +286,43 @@ public interface JdbcOperations {
|
||||
return queryFirst(sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询,只取结果集第一行第一列的值,映射为指定类型并返回。
|
||||
* 如果查询结果为空,则返回指定的默认值。
|
||||
* 适用于 {@code SELECT COUNT(*)}、{@code SELECT MAX(...)} 等聚合查询场景。
|
||||
*
|
||||
* @param <T> 目标类型
|
||||
* @param sql SQL
|
||||
* @param params 参数
|
||||
* @param clazz 目标类型
|
||||
* @param defaultValue 查询结果为空时返回的默认值
|
||||
*
|
||||
* @return 第一行第一列的值,如果查询结果为空则返回 {@code defaultValue}
|
||||
* @throws SQLException SQL 异常
|
||||
*/
|
||||
default <T> T queryValueOrDefault(String sql, Object[] params, Class<T> clazz, T defaultValue)
|
||||
throws SQLException {
|
||||
return queryValue(sql, params, clazz).orElse(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询,只取结果集第一行第一列的值,映射为指定类型并返回。
|
||||
* 如果查询结果为空,则返回指定的默认值。
|
||||
* 适用于 {@code SELECT COUNT(*)}、{@code SELECT MAX(...)} 等聚合查询场景。
|
||||
*
|
||||
* @param <T> 目标类型
|
||||
* @param sql SQL
|
||||
* @param clazz 目标类型
|
||||
* @param defaultValue 查询结果为空时返回的默认值
|
||||
*
|
||||
* @return 第一行第一列的值,如果查询结果为空则返回 {@code defaultValue}
|
||||
* @throws SQLException SQL 异常
|
||||
*/
|
||||
default <T> T queryValueOrDefault(String sql, Class<T> clazz, T defaultValue)
|
||||
throws SQLException {
|
||||
return queryValueOrDefault(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询第一行第一列并转换为 boolean
|
||||
*
|
||||
|
||||
@@ -60,9 +60,9 @@ public interface RowMapper<T> {
|
||||
* @param <T> Java Bean 的类型
|
||||
*
|
||||
* @return {@link DefaultBeanRowMapper}
|
||||
* @throws SQLException 如果创建 {@link DefaultBeanRowMapper} 失败
|
||||
* @throws IllegalStateException 如果创建 {@link DefaultBeanRowMapper} 失败
|
||||
*/
|
||||
static <T> RowMapper<T> beanRowMapper(Class<T> beanType) throws SQLException {
|
||||
static <T> RowMapper<T> beanRowMapper(Class<T> beanType) {
|
||||
return DefaultBeanRowMapper.of(beanType);
|
||||
}
|
||||
|
||||
@@ -74,10 +74,9 @@ public interface RowMapper<T> {
|
||||
* @param <T> Java Bean 的类型
|
||||
*
|
||||
* @return {@link DefaultBeanRowMapper}
|
||||
* @throws SQLException 如果创建 {@link DefaultBeanRowMapper} 失败
|
||||
* @throws IllegalStateException 如果创建 {@link DefaultBeanRowMapper} 失败
|
||||
*/
|
||||
static <T> RowMapper<T> beanRowMapper(Class<T> beanType, Map<String, String> propertyColMap)
|
||||
throws SQLException {
|
||||
static <T> RowMapper<T> beanRowMapper(Class<T> beanType, Map<String, String> propertyColMap) {
|
||||
return DefaultBeanRowMapper.of(beanType, propertyColMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,10 +96,10 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, Object[] params, Class<T> clazz)
|
||||
public <T> List<T> queryValues(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport.queryList(conn, sql, params, clazz);
|
||||
return JdbcOperationSupport.queryValues(conn, sql, params, clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,10 +128,10 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> Optional<T> queryFirst(String sql, Object[] params, Class<T> clazz)
|
||||
public <T> Optional<T> queryValue(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final T result = JdbcOperationSupport.queryFirst(conn, sql, params, clazz);
|
||||
final T result = JdbcOperationSupport.queryValue(conn, sql, params, clazz);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final Boolean result = JdbcOperationSupport
|
||||
.queryFirst(conn, sql, params, Boolean.class);
|
||||
.queryValue(conn, sql, params, Boolean.class);
|
||||
return Boolean.TRUE.equals(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,9 +181,9 @@ public class TransactionTemplate {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, Object[] params, Class<T> clazz)
|
||||
public <T> List<T> queryValues(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return JdbcOperationSupport.queryList(this.conn, sql, params, clazz);
|
||||
return JdbcOperationSupport.queryValues(this.conn, sql, params, clazz);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@@ -207,9 +207,9 @@ public class TransactionTemplate {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> Optional<T> queryFirst(String sql, Object[] params, Class<T> clazz)
|
||||
public <T> Optional<T> queryValue(String sql, Object[] params, Class<T> clazz)
|
||||
throws SQLException {
|
||||
final T result = JdbcOperationSupport.queryFirst(this.conn, sql, params, clazz);
|
||||
final T result = JdbcOperationSupport.queryValue(this.conn, sql, params, clazz);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ public class TransactionTemplate {
|
||||
public boolean queryBoolean(String sql, Object[] params)
|
||||
throws SQLException {
|
||||
final Boolean result = JdbcOperationSupport
|
||||
.queryFirst(this.conn, sql, params, Boolean.class);
|
||||
.queryValue(this.conn, sql, params, Boolean.class);
|
||||
return Boolean.TRUE.equals(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ class BatchUpdateTest extends BaseH2Test {
|
||||
void testBatchUpdateQuietlyFalseInterrupted() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
int count0 = template.queryFirst("SELECT COUNT(*) FROM users", Integer.class)
|
||||
int count0 = template.queryValue("SELECT COUNT(*) FROM users", Integer.class)
|
||||
.orElse(0);
|
||||
|
||||
List<Object[]> params = buildBatchParams(userListContainingInvalidData, a -> new Object[] { a.getUsername(), a.getEmail(), a.getAge(), a.getBalance(), a.getActive() });
|
||||
@@ -231,7 +231,7 @@ class BatchUpdateTest extends BaseH2Test {
|
||||
assertNull(result.getUpdateCounts(3));
|
||||
assertNull(result.getUpdateCounts(4));
|
||||
|
||||
Optional<Integer> count8 = template.queryFirst("SELECT COUNT(*) FROM users", Integer.class);
|
||||
Optional<Integer> count8 = template.queryValue("SELECT COUNT(*) FROM users", Integer.class);
|
||||
assertEquals(count0 + 8, count8.get().intValue());
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ class BatchUpdateTest extends BaseH2Test {
|
||||
void testBatchUpdateQuietlyTrue() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
int count0 = template.queryFirst("SELECT COUNT(*) FROM users", Integer.class)
|
||||
int count0 = template.queryValue("SELECT COUNT(*) FROM users", Integer.class)
|
||||
.orElse(0);
|
||||
|
||||
List<Object[]> params = buildBatchParams(userListContainingInvalidData, a -> new Object[] { a.getUsername(), a.getEmail(), a.getAge(), a.getBalance(), a.getActive() });
|
||||
@@ -261,7 +261,7 @@ class BatchUpdateTest extends BaseH2Test {
|
||||
assertArrayEquals(new int[] { Statement.EXECUTE_FAILED, 1, 1 }, result.getUpdateCounts(3));
|
||||
assertArrayEquals(new int[] { 1 }, result.getUpdateCounts(4));
|
||||
|
||||
Optional<Integer> count11 = template.queryFirst("SELECT COUNT(*) FROM users", Integer.class);
|
||||
Optional<Integer> count11 = template.queryValue("SELECT COUNT(*) FROM users", Integer.class);
|
||||
assertEquals(count0 + 11, count11.get().intValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import xyz.zhouxy.jdbc.ResultHandler;
|
||||
import xyz.zhouxy.jdbc.SimpleJdbcTemplate;
|
||||
|
||||
/**
|
||||
* 查询 API 测试:query、queryList、queryFirst、queryBoolean。
|
||||
* 查询 API 测试:query、queryList、queryFirst、queryValues、queryValue、queryBoolean。
|
||||
*/
|
||||
@DisplayName("SimpleJdbcTemplate 查询操作")
|
||||
class QueryTest extends BaseH2Test {
|
||||
@@ -119,28 +119,28 @@ class QueryTest extends BaseH2Test {
|
||||
assertEquals(5, users.size());
|
||||
}
|
||||
|
||||
// ==================== queryList(Class) ====================
|
||||
// ==================== queryValues(Class) ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("queryList(Class):单列查询返回 String 列表")
|
||||
void testQueryListWithClassString() throws SQLException {
|
||||
@DisplayName("queryValues(Class):单列查询返回 String 列表")
|
||||
void testQueryValuesWithClassString() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
List<String> usernames = template.queryList(
|
||||
List<String> usernames = template.queryValues(
|
||||
"SELECT username FROM users ORDER BY id",
|
||||
String.class);
|
||||
|
||||
logger.info("queryList(Class) 返回用户名: {}", usernames);
|
||||
logger.info("queryValues(Class) 返回用户名: {}", usernames);
|
||||
assertEquals(5, usernames.size());
|
||||
assertTrue(usernames.contains("alice"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryList(Class):空结果集返回空列表")
|
||||
void testQueryListEmptyResult() throws SQLException {
|
||||
@DisplayName("queryValues(Class):空结果集返回空列表")
|
||||
void testQueryValuesEmptyResult() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
List<String> result = template.queryList(
|
||||
List<String> result = template.queryValues(
|
||||
"SELECT username FROM users WHERE id = ?",
|
||||
buildParams(999), String.class);
|
||||
|
||||
@@ -215,14 +215,14 @@ class QueryTest extends BaseH2Test {
|
||||
assertTrue(user.isPresent());
|
||||
}
|
||||
|
||||
// ==================== queryFirst(Class) ====================
|
||||
// ==================== queryValue(Class) ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("queryFirst(Class):查询第一行第一列")
|
||||
void testQueryFirstWithClass() throws SQLException {
|
||||
@DisplayName("queryValue(Class):查询第一行第一列")
|
||||
void testQueryValueWithClass() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
Optional<String> username = template.queryFirst(
|
||||
Optional<String> username = template.queryValue(
|
||||
"SELECT username FROM users ORDER BY id",
|
||||
String.class);
|
||||
|
||||
@@ -231,11 +231,11 @@ class QueryTest extends BaseH2Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryFirst(Class):空结果返回 Optional.empty()")
|
||||
void testQueryFirstClassEmpty() throws SQLException {
|
||||
@DisplayName("queryValue(Class):空结果返回 Optional.empty()")
|
||||
void testQueryValueClassEmpty() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
Optional<String> result = template.queryFirst(
|
||||
Optional<String> result = template.queryValue(
|
||||
"SELECT username FROM users WHERE id = ?",
|
||||
buildParams(999), String.class);
|
||||
|
||||
@@ -244,11 +244,11 @@ class QueryTest extends BaseH2Test {
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("queryFirst + Class:统计总行数")
|
||||
void testQueryFirstWithClass_queryCount() throws SQLException {
|
||||
@DisplayName("queryValue + Class:统计总行数")
|
||||
void testQueryValueWithClass_queryCount() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
int count = template.queryFirst(
|
||||
int count = template.queryValue(
|
||||
"SELECT COUNT(*) FROM users",
|
||||
new Object[0],
|
||||
Integer.class)
|
||||
@@ -259,11 +259,11 @@ class QueryTest extends BaseH2Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryFirst + Class:聚合求和")
|
||||
void testQueryFirstWithClass_queryAggregation() throws SQLException {
|
||||
@DisplayName("queryValue + Class:聚合求和")
|
||||
void testQueryValueWithClass_queryAggregation() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
Long totalBalance = template.queryFirst(
|
||||
Long totalBalance = template.queryValue(
|
||||
"SELECT SUM(balance) FROM users",
|
||||
new Object[0],
|
||||
Long.class)
|
||||
@@ -273,6 +273,81 @@ class QueryTest extends BaseH2Test {
|
||||
assertNotNull(totalBalance);
|
||||
}
|
||||
|
||||
// ==================== queryValueOrDefault ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("queryValueOrDefault:有结果时返回值")
|
||||
void testQueryValueOrDefaultWithResult() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
String username = template.queryValueOrDefault(
|
||||
"SELECT username FROM users WHERE id = ?",
|
||||
new Object[]{1}, String.class, "default");
|
||||
|
||||
assertEquals("alice", username);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryValueOrDefault:无结果时返回默认值")
|
||||
void testQueryValueOrDefaultWithDefault() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
String username = template.queryValueOrDefault(
|
||||
"SELECT username FROM users WHERE id = ?",
|
||||
new Object[]{999}, String.class, "unknown");
|
||||
|
||||
assertEquals("unknown", username);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryValueOrDefault:COUNT 聚合查询")
|
||||
void testQueryValueOrDefaultCount() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
long count = template.queryValueOrDefault(
|
||||
"SELECT COUNT(*) FROM users",
|
||||
new Object[0], Long.class, 0L);
|
||||
|
||||
assertEquals(5L, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryValueOrDefault:SUM 聚合查询")
|
||||
void testQueryValueOrDefaultSum() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
long totalBalance = template.queryValueOrDefault(
|
||||
"SELECT SUM(balance) FROM users",
|
||||
new Object[0], Long.class, 0L);
|
||||
|
||||
assertTrue(totalBalance > 0);
|
||||
logger.info("queryValueOrDefault SUM 结果: {}", totalBalance);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryValueOrDefault:无参数重载")
|
||||
void testQueryValueOrDefaultNoParams() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
long count = template.queryValueOrDefault(
|
||||
"SELECT COUNT(*) FROM users",
|
||||
Long.class, 0L);
|
||||
|
||||
assertEquals(5L, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("queryValueOrDefault:空表 COUNT 返回默认值 0")
|
||||
void testQueryValueOrDefaultEmptyTable() throws SQLException {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
long count = template.queryValueOrDefault(
|
||||
"SELECT COUNT(*) FROM users WHERE id = ?",
|
||||
new Object[]{999}, Long.class, 0L);
|
||||
|
||||
assertEquals(0L, count);
|
||||
}
|
||||
|
||||
// ==================== queryFirst(Map) ====================
|
||||
|
||||
@Test
|
||||
@@ -385,7 +460,7 @@ class QueryTest extends BaseH2Test {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
assertThrows(SQLException.class, () ->
|
||||
template.queryList("SELECT * FROM non_existent_table",
|
||||
template.queryValues("SELECT * FROM non_existent_table",
|
||||
new Object[0], String.class));
|
||||
}
|
||||
|
||||
@@ -395,7 +470,7 @@ class QueryTest extends BaseH2Test {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
assertThrows(SQLException.class, () ->
|
||||
template.queryList("SELEC * FROM users",
|
||||
template.queryValues("SELEC * FROM users",
|
||||
new Object[0], String.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -137,9 +137,9 @@ class RowMapperTest extends BaseH2Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("DefaultBeanRowMapper:无无参构造器的 Bean 抛出 SQLException")
|
||||
@DisplayName("DefaultBeanRowMapper:无无参构造器的 Bean 抛出 IllegalStateException")
|
||||
void testDefaultBeanRowMapperNoNoArgConstructor() {
|
||||
assertThrows(SQLException.class, () ->
|
||||
assertThrows(IllegalStateException.class, () ->
|
||||
DefaultBeanRowMapper.of(BeanWithoutNoArgConstructor.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ class TransactionTest extends BaseH2Test {
|
||||
});
|
||||
|
||||
// 验证事务已提交
|
||||
Optional<String> newUser = template.queryFirst(
|
||||
Optional<String> newUser = template.queryValue(
|
||||
"SELECT username FROM users WHERE username = ?",
|
||||
buildParams("txUser1"), String.class);
|
||||
assertTrue(newUser.isPresent());
|
||||
|
||||
Optional<Long> balance = template.queryFirst(
|
||||
Optional<Long> balance = template.queryValue(
|
||||
"SELECT balance FROM users WHERE username = ?",
|
||||
buildParams("alice"), Long.class);
|
||||
assertEquals(Long.valueOf(99999L), balance.orElse(null));
|
||||
@@ -65,7 +65,7 @@ class TransactionTest extends BaseH2Test {
|
||||
SimpleJdbcTemplate template = createTemplate();
|
||||
|
||||
// 记录原始 balance
|
||||
Optional<Long> originalBalance = template.queryFirst(
|
||||
Optional<Long> originalBalance = template.queryValue(
|
||||
"SELECT balance FROM users WHERE username = ?",
|
||||
buildParams("alice"), Long.class);
|
||||
|
||||
@@ -85,13 +85,13 @@ class TransactionTest extends BaseH2Test {
|
||||
assertEquals("模拟业务异常", ex.getCause().getMessage());
|
||||
|
||||
// 验证更新已回滚
|
||||
Optional<Long> currentBalance = template.queryFirst(
|
||||
Optional<Long> currentBalance = template.queryValue(
|
||||
"SELECT balance FROM users WHERE username = ?",
|
||||
buildParams("alice"), Long.class);
|
||||
assertEquals(originalBalance.orElse(null), currentBalance.orElse(null));
|
||||
|
||||
// 验证插入已回滚
|
||||
Optional<String> rolledBackUser = template.queryFirst(
|
||||
Optional<String> rolledBackUser = template.queryValue(
|
||||
"SELECT username FROM users WHERE username = ?",
|
||||
buildParams("txUser2"), String.class);
|
||||
assertFalse(rolledBackUser.isPresent());
|
||||
@@ -114,7 +114,7 @@ class TransactionTest extends BaseH2Test {
|
||||
|
||||
// 验证插入已回滚
|
||||
assertDoesNotThrow(() -> {
|
||||
Optional<String> user = template.queryFirst(
|
||||
Optional<String> user = template.queryValue(
|
||||
"SELECT username FROM users WHERE username = ?",
|
||||
buildParams("validUser"), String.class);
|
||||
assertFalse(user.isPresent());
|
||||
@@ -135,7 +135,7 @@ class TransactionTest extends BaseH2Test {
|
||||
});
|
||||
|
||||
// 验证数据已持久化
|
||||
Optional<String> user = template.queryFirst(
|
||||
Optional<String> user = template.queryValue(
|
||||
"SELECT username FROM users WHERE username = ?",
|
||||
buildParams("cftUser"), String.class);
|
||||
assertTrue(user.isPresent());
|
||||
@@ -155,7 +155,7 @@ class TransactionTest extends BaseH2Test {
|
||||
});
|
||||
|
||||
// 验证数据已回滚
|
||||
Optional<String> user = template.queryFirst(
|
||||
Optional<String> user = template.queryValue(
|
||||
"SELECT username FROM users WHERE username = ?",
|
||||
buildParams("cffUser"), String.class);
|
||||
assertFalse(user.isPresent());
|
||||
@@ -177,7 +177,7 @@ class TransactionTest extends BaseH2Test {
|
||||
|
||||
// 验证回滚
|
||||
assertDoesNotThrow(() -> {
|
||||
Optional<String> user = template.queryFirst(
|
||||
Optional<String> user = template.queryValue(
|
||||
"SELECT username FROM users WHERE username = ?",
|
||||
buildParams("exUser"), String.class);
|
||||
assertFalse(user.isPresent());
|
||||
@@ -196,7 +196,7 @@ class TransactionTest extends BaseH2Test {
|
||||
buildParams("visible", "visible@test.com"));
|
||||
|
||||
// 在同一事务内可以查询到刚插入的数据
|
||||
Optional<String> user = ops.queryFirst(
|
||||
Optional<String> user = ops.queryValue(
|
||||
"SELECT username FROM users WHERE username = ?",
|
||||
buildParams("visible"), String.class);
|
||||
assertTrue(user.isPresent());
|
||||
|
||||
Reference in New Issue
Block a user