forked from plusone/simple-jdbc
refactor: 优化JDBC操作实现并改进事务异常处理
- 修改BatchUpdateResult类方法访问权限为包私有 - 在DefaultBeanRowMapper中使用getColumnLabel替代getColumnName - 为JdbcOperations接口添加默认实现方法,简化API调用 - 添加TransactionException类用于包装事务执行过程中的异常
This commit is contained in:
@@ -33,7 +33,7 @@ public class BatchUpdateResult {
|
||||
|
||||
private int completeBatchCount;
|
||||
|
||||
public BatchUpdateResult(int total, int batchCount, int batchSize) {
|
||||
BatchUpdateResult(int total, int batchCount, int batchSize) {
|
||||
this.total = total;
|
||||
this.batchCount = batchCount;
|
||||
this.batchSize = batchSize;
|
||||
@@ -45,7 +45,7 @@ public class BatchUpdateResult {
|
||||
/**
|
||||
* 记录成功批次
|
||||
*/
|
||||
public void recordSuccessBatch(int batchIndex, int[] updateCounts) {
|
||||
void recordSuccessBatch(int batchIndex, int[] updateCounts) {
|
||||
this.completeBatchCount++;
|
||||
this.allUpdateCounts.put(batchIndex, updateCounts);
|
||||
this.successBatchCount++;
|
||||
@@ -54,7 +54,7 @@ public class BatchUpdateResult {
|
||||
/**
|
||||
* 记录失败批次
|
||||
*/
|
||||
public void recordErrorBatch(int batchIndex, int[] updateCounts, Throwable cause) {
|
||||
void recordErrorBatch(int batchIndex, int[] updateCounts, Throwable cause) {
|
||||
this.completeBatchCount++;
|
||||
this.allUpdateCounts.put(batchIndex, updateCounts);
|
||||
this.allErrorsInfo.put(batchIndex, new BatchUpdateErrorInfo(batchIndex, cause));
|
||||
@@ -66,7 +66,7 @@ public class BatchUpdateResult {
|
||||
/**
|
||||
* 中断
|
||||
*/
|
||||
public void interrupt() {
|
||||
void interrupt() {
|
||||
this.status = BatchUpdateStatus.INTERRUPTED;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
|
||||
* <li>使用反射获取类型信息,也是使用反射调用无参构造器和 {@code setter} 方法。</li>
|
||||
* <li>{@code propertyColMap} 未指定的列名和属性名的映射时,默认 JavaBean 的属性名为小驼峰,列名为小写蛇形命名。</li>
|
||||
* <li>从{@link ResultSet} 中获取属性值时,使用 {@link ResultSet#getObject(String, Class)} 获取。</li>
|
||||
* <li>JavaBean 属性仅支持引用类型,不支持基本数据类型。</li>
|
||||
* <li>实际使用中还是建议针对目标类型自定义 {@link RowMapper}。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
@@ -119,7 +120,7 @@ public class DefaultBeanRowMapper<T> implements RowMapper<T> {
|
||||
ResultSetMetaData metaData = rs.getMetaData();
|
||||
// 遍历结果的每一列
|
||||
for (int i = 1; i <= metaData.getColumnCount(); i++) {
|
||||
String colName = metaData.getColumnName(i);
|
||||
String colName = metaData.getColumnLabel(i);
|
||||
// 获取查询结果列名对应的属性,调用 setter
|
||||
PropertyDescriptor propertyDescriptor = this.colPropertyMap.get(colName);
|
||||
if (propertyDescriptor != null) {
|
||||
|
||||
@@ -25,8 +25,10 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.*;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -140,6 +142,9 @@ class JdbcOperationSupport {
|
||||
/**
|
||||
* 查询第一行第一列并转换为 boolean
|
||||
*
|
||||
* <p>
|
||||
* <b>注:如果查询结果为空,则返回 {@code false}。</b>
|
||||
*
|
||||
* @param conn 数据库连接
|
||||
* @param sql SQL
|
||||
* @param params 参数
|
||||
@@ -187,7 +192,7 @@ class JdbcOperationSupport {
|
||||
assertConnectionNotNull(conn);
|
||||
assertSqlNotNull(sql);
|
||||
assertRowMapperNotNull(rowMapper);
|
||||
final List<T> result = new LinkedList<>();
|
||||
final List<T> result = Lists.newArrayList();
|
||||
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||
fillStatement(stmt, params);
|
||||
stmt.executeUpdate();
|
||||
@@ -209,7 +214,6 @@ class JdbcOperationSupport {
|
||||
* @param sql sql语句
|
||||
* @param params 参数列表
|
||||
* @param batchSize 每次批量更新的数据量
|
||||
* @param exceptions 空列表,用于记录异常信息
|
||||
* @param quietly 静默分批更新。
|
||||
* 如果 {@code quietly} 为 {@code true},分批更新过程中发生异常不中断操作;
|
||||
* 如果 {@code quietly} 为 {@code false},分批更新过程中发生异常即中断操作,并返回结果。
|
||||
@@ -313,7 +317,7 @@ class JdbcOperationSupport {
|
||||
@Nonnull RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
return queryInternal(conn, sql, params, rs -> {
|
||||
List<T> result = new LinkedList<>();
|
||||
List<T> result = Lists.newArrayList();
|
||||
int rowNumber = 0;
|
||||
while (rs.next()) {
|
||||
T e = rowMapper.mapRow(rs, rowNumber++);
|
||||
@@ -352,7 +356,10 @@ class JdbcOperationSupport {
|
||||
Object param;
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
param = params[i];
|
||||
if (param instanceof java.sql.Date) {
|
||||
if (param == null) {
|
||||
stmt.setObject(i + 1, null, Types.OTHER);
|
||||
}
|
||||
else if (param instanceof java.sql.Date) {
|
||||
stmt.setDate(i + 1, (java.sql.Date) param);
|
||||
}
|
||||
else if (param instanceof java.sql.Time) {
|
||||
|
||||
@@ -55,8 +55,10 @@ public interface JdbcOperations {
|
||||
* @param sql SQL
|
||||
* @param resultHandler 结果处理器,用于处理 {@link ResultSet}
|
||||
*/
|
||||
<T> T query(String sql, ResultHandler<T> resultHandler)
|
||||
throws SQLException;
|
||||
default <T> T query(String sql, ResultHandler<T> resultHandler)
|
||||
throws SQLException {
|
||||
return query(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
@@ -97,8 +99,10 @@ public interface JdbcOperations {
|
||||
* @param sql SQL
|
||||
* @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑
|
||||
*/
|
||||
<T> List<T> queryList(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException;
|
||||
default <T> List<T> queryList(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
return queryList(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询,返回结果映射为指定的类型。当结果为单列时使用
|
||||
@@ -106,16 +110,20 @@ public interface JdbcOperations {
|
||||
* @param sql SQL
|
||||
* @param clazz 将结果映射为指定的类型
|
||||
*/
|
||||
<T> List<T> queryList(String sql, Class<T> clazz)
|
||||
throws SQLException;
|
||||
default <T> List<T> queryList(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryList(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询,每一行数据映射为 {@code Map<String, Object>},返回结果列表
|
||||
*
|
||||
* @param sql SQL
|
||||
*/
|
||||
List<Map<String, Object>> queryList(String sql)
|
||||
throws SQLException;
|
||||
default List<Map<String, Object>> queryList(String sql)
|
||||
throws SQLException {
|
||||
return queryList(sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
@@ -157,8 +165,10 @@ public interface JdbcOperations {
|
||||
* @param sql SQL
|
||||
* @param rowMapper {@link ResultSet} 中每一行的数据的处理逻辑
|
||||
*/
|
||||
<T> Optional<T> queryFirst(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException;
|
||||
default <T> Optional<T> queryFirst(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
return queryFirst(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询第一行第一列,并转换为指定类型
|
||||
@@ -167,28 +177,40 @@ public interface JdbcOperations {
|
||||
* @param sql SQL
|
||||
* @param clazz 目标类型
|
||||
*/
|
||||
<T> Optional<T> queryFirst(String sql, Class<T> clazz)
|
||||
throws SQLException;
|
||||
default <T> Optional<T> queryFirst(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return queryFirst(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行查询,将第一行数据转为 Map<String, Object>
|
||||
*
|
||||
* @param sql SQL
|
||||
*/
|
||||
Optional<Map<String, Object>> queryFirst(String sql)
|
||||
throws SQLException;
|
||||
default Optional<Map<String, Object>> queryFirst(String sql)
|
||||
throws SQLException {
|
||||
return queryFirst(sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询第一行第一列并转换为 boolean
|
||||
*
|
||||
* <p>
|
||||
* <b>注:如果查询结果为空,则返回 {@code false}。</b>
|
||||
*
|
||||
* @param sql SQL
|
||||
*/
|
||||
boolean queryBoolean(String sql)
|
||||
throws SQLException;
|
||||
default boolean queryBoolean(String sql)
|
||||
throws SQLException {
|
||||
return queryBoolean(sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询第一行第一列并转换为 boolean
|
||||
*
|
||||
* <p>
|
||||
* <b>注:如果查询结果为空,则返回 {@code false}。</b>
|
||||
*
|
||||
* @param sql SQL
|
||||
*/
|
||||
boolean queryBoolean(String sql, Object[] params)
|
||||
@@ -214,8 +236,10 @@ public interface JdbcOperations {
|
||||
* @param sql 要执行的 SQL
|
||||
* @return 更新记录数
|
||||
*/
|
||||
int update(String sql)
|
||||
throws SQLException;
|
||||
default int update(String sql)
|
||||
throws SQLException {
|
||||
return update(sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 SQL 并返回生成的 keys
|
||||
@@ -239,8 +263,10 @@ public interface JdbcOperations {
|
||||
* @return generated keys
|
||||
* @throws SQLException 执行 SQL 遇到异常情况将抛出
|
||||
*/
|
||||
<T> List<T> update(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException;
|
||||
default <T> List<T> update(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
return update(sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新
|
||||
@@ -261,7 +287,6 @@ public interface JdbcOperations {
|
||||
* @param sql sql语句
|
||||
* @param params 参数列表
|
||||
* @param batchSize 每次批量更新的数据量
|
||||
* @param exceptions 空列表,用于记录异常信息
|
||||
* @param quietly 静默分批更新。
|
||||
* 如果 {@code quietly} 为 {@code true},分批更新过程中发生异常不中断操作;
|
||||
* 如果 {@code quietly} 为 {@code false},分批更新过程中发生异常即中断操作,并返回结果。
|
||||
|
||||
@@ -36,13 +36,18 @@ import java.util.Map;
|
||||
public interface RowMapper<T> {
|
||||
T mapRow(ResultSet rs, int rowNumber) throws SQLException;
|
||||
|
||||
/** 每一行数据转换为 {@link HashMap} */
|
||||
/**
|
||||
* 每一行数据转换为 {@link HashMap}
|
||||
*
|
||||
* <p>
|
||||
* <b>注:如果两个属性映射到同一列名(虽然不常见),后者静默覆盖前者。</b>
|
||||
*/
|
||||
RowMapper<Map<String, Object>> HASH_MAP_MAPPER = (rs, rowNumber) -> {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
ResultSetMetaData metaData = rs.getMetaData();
|
||||
int columnCount = metaData.getColumnCount();
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
String colName = metaData.getColumnName(i);
|
||||
String colName = metaData.getColumnLabel(i);
|
||||
result.put(colName, rs.getObject(colName));
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -61,15 +61,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> T query(String sql, ResultHandler<T> resultHandler)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport.query(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region - queryList
|
||||
@@ -101,34 +92,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public List<Map<String, Object>> queryList(String sql)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport
|
||||
.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER);
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region - queryFirst
|
||||
@@ -166,51 +129,7 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> Optional<T> queryFirst(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final T result = JdbcOperationSupport
|
||||
.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> Optional<T> queryFirst(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final T result = JdbcOperationSupport
|
||||
.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Optional<Map<String, Object>> queryFirst(String sql)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final Map<String, Object> result = JdbcOperationSupport
|
||||
.queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean queryBoolean(String sql) // TODO 单元测试
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final Boolean result = JdbcOperationSupport
|
||||
.queryFirstBoolean(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
return Boolean.TRUE.equals(result);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean queryBoolean(String sql, Object[] params) // TODO 单元测试
|
||||
public boolean queryBoolean(String sql, Object[] params)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final Boolean result = JdbcOperationSupport
|
||||
@@ -232,15 +151,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int update(String sql)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport.update(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> update(String sql, Object[] params, RowMapper<T> rowMapper)
|
||||
@@ -250,15 +160,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> update(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport.update(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public BatchUpdateResult batchUpdate(String sql, @Nullable Collection<Object[]> params, int batchSize)
|
||||
@@ -271,7 +172,7 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public BatchUpdateResult batchUpdate(String sql, @Nullable Collection<Object[]> params,
|
||||
int batchSize, boolean quietly)
|
||||
int batchSize, boolean quietly)
|
||||
throws SQLException {
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
return JdbcOperationSupport
|
||||
@@ -290,14 +191,14 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
* operations 中使用 JdbcExecutor 实参进行 JDBC 操作,这些操作在一个连接中
|
||||
* </p>
|
||||
*
|
||||
* @param <E> 异常类型
|
||||
* @param operations 事务操作
|
||||
* @throws SQLException SQL 异常
|
||||
* @throws E 事务中的异常
|
||||
* @param <E> 异常类型
|
||||
* @param operations 事务操作
|
||||
* @throws SQLException SQL 异常
|
||||
* @throws TransactionException 事务异常。事务中的异常会包装在该异常中。
|
||||
*/
|
||||
public <E extends Exception> void executeTransaction(
|
||||
@Nonnull final ThrowingConsumer<JdbcOperations, E> operations)
|
||||
throws SQLException, E {
|
||||
throws TransactionException, SQLException {
|
||||
AssertTools.checkNotNull(operations, "Operations can not be null.");
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final boolean autoCommit = conn.getAutoCommit();
|
||||
@@ -305,12 +206,10 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
conn.setAutoCommit(false);
|
||||
operations.accept(new JdbcExecutor(conn));
|
||||
conn.commit();
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
conn.rollback();
|
||||
throw new SQLException("Transaction failed during execution", e);
|
||||
}
|
||||
finally {
|
||||
throw new TransactionException(e);
|
||||
} finally {
|
||||
conn.setAutoCommit(autoCommit);
|
||||
}
|
||||
}
|
||||
@@ -321,14 +220,14 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
* 如果 {@code operations} 返回 {@code true},则提交事务;
|
||||
* 如果抛出异常,或返回 {@code false},则回滚事务
|
||||
*
|
||||
* @param <E> 事务中的异常
|
||||
* @param operations 事务操作
|
||||
* @throws SQLException 数据库异常
|
||||
* @throws E 事务中的异常类型
|
||||
* @param <E> 事务中的异常
|
||||
* @param operations 事务操作
|
||||
* @throws SQLException 数据库异常
|
||||
* @throws TransactionException 事务异常。事务中的异常会包装在该异常中。
|
||||
*/
|
||||
public <E extends Exception> void commitIfTrue(
|
||||
@Nonnull final ThrowingPredicate<JdbcOperations, E> operations)
|
||||
throws SQLException, E {
|
||||
throws SQLException, TransactionException {
|
||||
AssertTools.checkNotNull(operations, "Operations can not be null.");
|
||||
try (Connection conn = this.dataSource.getConnection()) {
|
||||
final boolean autoCommit = conn.getAutoCommit();
|
||||
@@ -336,16 +235,13 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
conn.setAutoCommit(false);
|
||||
if (operations.test(new JdbcExecutor(conn))) {
|
||||
conn.commit();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
conn.rollback();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
conn.rollback();
|
||||
throw new SQLException("Transaction failed during execution", e);
|
||||
}
|
||||
finally {
|
||||
throw new TransactionException(e);
|
||||
} finally {
|
||||
conn.setAutoCommit(autoCommit);
|
||||
}
|
||||
}
|
||||
@@ -370,14 +266,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
return JdbcOperationSupport.query(this.conn, sql, params, resultHandler);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> T query(String sql, ResultHandler<T> resultHandler)
|
||||
throws SQLException {
|
||||
return JdbcOperationSupport
|
||||
.query(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region - queryList
|
||||
@@ -403,29 +291,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
return JdbcOperationSupport.queryList(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
return JdbcOperationSupport
|
||||
.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> queryList(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
return JdbcOperationSupport.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public List<Map<String, Object>> queryList(String sql)
|
||||
throws SQLException {
|
||||
return JdbcOperationSupport
|
||||
.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region - queryFirst
|
||||
@@ -455,42 +320,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> Optional<T> queryFirst(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
final T result = JdbcOperationSupport
|
||||
.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> Optional<T> queryFirst(String sql, Class<T> clazz)
|
||||
throws SQLException {
|
||||
final T result = JdbcOperationSupport
|
||||
.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Optional<Map<String, Object>> queryFirst(String sql)
|
||||
throws SQLException {
|
||||
final Map<String, Object> result = JdbcOperationSupport
|
||||
.queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER);
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean queryBoolean(String sql)
|
||||
throws SQLException {
|
||||
final Boolean result = JdbcOperationSupport
|
||||
.queryFirstBoolean(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
return Boolean.TRUE.equals(result);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean queryBoolean(String sql, Object[] params)
|
||||
@@ -510,13 +339,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
return JdbcOperationSupport.update(this.conn, sql, params);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int update(String sql)
|
||||
throws SQLException {
|
||||
return JdbcOperationSupport.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> update(String sql, Object[] params, RowMapper<T> rowMapper)
|
||||
@@ -524,13 +346,6 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
return JdbcOperationSupport.update(this.conn, sql, params, rowMapper);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public <T> List<T> update(String sql, RowMapper<T> rowMapper)
|
||||
throws SQLException {
|
||||
return JdbcOperationSupport.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public BatchUpdateResult batchUpdate(String sql, @Nullable Collection<Object[]> params, int batchSize)
|
||||
@@ -541,9 +356,9 @@ public class SimpleJdbcTemplate implements JdbcOperations {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public BatchUpdateResult batchUpdate(String sql,
|
||||
@Nullable Collection<Object[]> params,
|
||||
int batchSize,
|
||||
boolean quietly) throws SQLException {
|
||||
@Nullable Collection<Object[]> params,
|
||||
int batchSize,
|
||||
boolean quietly) throws SQLException {
|
||||
return JdbcOperationSupport
|
||||
.batchUpdate(this.conn, sql, params, batchSize, quietly);
|
||||
}
|
||||
|
||||
48
src/main/java/xyz/zhouxy/jdbc/TransactionException.java
Normal file
48
src/main/java/xyz/zhouxy/jdbc/TransactionException.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2026 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;
|
||||
|
||||
/**
|
||||
* 事务异常
|
||||
*
|
||||
* <p>
|
||||
* 用于包装事务执行过程中发生的原始异常
|
||||
*
|
||||
* @author ZhouXY108 <luquanlion@outlook.com>
|
||||
*/
|
||||
public class TransactionException extends Exception {
|
||||
private static final long serialVersionUID = 87276230526383501L;
|
||||
|
||||
/**
|
||||
* 事务异常
|
||||
*
|
||||
* @param cause 原始异常
|
||||
*/
|
||||
public TransactionException(Throwable cause) {
|
||||
super("Transaction failed during execution", cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* 事务异常
|
||||
*
|
||||
* @param message 异常信息
|
||||
* @param cause 原始异常
|
||||
*/
|
||||
public TransactionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import com.google.common.io.Resources;
|
||||
import xyz.zhouxy.jdbc.JdbcOperations;
|
||||
import xyz.zhouxy.jdbc.RowMapper;
|
||||
import xyz.zhouxy.jdbc.SimpleJdbcTemplate;
|
||||
import xyz.zhouxy.jdbc.TransactionException;
|
||||
import xyz.zhouxy.plusone.commons.util.IdGenerator;
|
||||
import xyz.zhouxy.plusone.commons.util.IdWorker;
|
||||
|
||||
@@ -146,20 +147,18 @@ class SimpleJdbcTemplateTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransaction() throws SQLException {
|
||||
void testTransaction() throws TransactionException, SQLException {
|
||||
// 抛异常,回滚
|
||||
{
|
||||
long id = this.idGenerator.nextId();
|
||||
try {
|
||||
TransactionException e = assertThrows(TransactionException.class, () -> {
|
||||
jdbcTemplate.executeTransaction((JdbcOperations jdbc) -> {
|
||||
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();
|
||||
});
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
assertEquals(NullPointerException.class, e.getCause().getClass());
|
||||
Optional<Map<String, Object>> first = jdbcTemplate
|
||||
.queryFirst("SELECT * FROM sys_account WHERE id = ?", buildParams(id));
|
||||
log.info("first: {}", first);
|
||||
@@ -183,16 +182,14 @@ class SimpleJdbcTemplateTests {
|
||||
// 抛异常,回滚
|
||||
{
|
||||
long id = this.idGenerator.nextId();
|
||||
try {
|
||||
TransactionException e = assertThrows(TransactionException.class, () -> {
|
||||
jdbcTemplate.commitIfTrue(jdbc -> {
|
||||
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();
|
||||
});
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
assertEquals(NullPointerException.class, e.getCause().getClass());
|
||||
Optional<Map<String, Object>> first = jdbcTemplate
|
||||
.queryFirst("SELECT * FROM sys_account WHERE id = ?", buildParams(id));
|
||||
log.info("first: {}", first);
|
||||
@@ -244,4 +241,15 @@ class SimpleJdbcTemplateTests {
|
||||
t.get());
|
||||
log.info("{}", t);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQueryBoolean() throws SQLException {
|
||||
// 建议写法
|
||||
assertTrue(jdbcTemplate.queryBoolean("SELECT EXISTS(SELECT 1 FROM sys_account WHERE id = 10)"));
|
||||
assertFalse(jdbcTemplate.queryBoolean("SELECT EXISTS(SELECT 1 FROM sys_account WHERE id = 999)"));
|
||||
|
||||
// 不建议写法
|
||||
assertTrue(jdbcTemplate.queryBoolean("SELECT 1 FROM sys_account WHERE id = 10"));
|
||||
assertFalse(jdbcTemplate.queryBoolean("SELECT 1 FROM sys_account WHERE id = 999"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
DROP TABLE IF EXISTS sys_account;
|
||||
|
||||
CREATE TABLE sys_account (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
|
||||
, `username` VARCHAR(255) NOT NULL
|
||||
@@ -7,4 +9,4 @@ CREATE TABLE sys_account (
|
||||
, `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
, `updated_by` BIGINT DEFAULT NULL
|
||||
, `version` BIGINT NOT NULL DEFAULT 0
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user