/* * Copyright 2022-2025 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.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.Nonnull; import javax.annotation.Nullable; import javax.sql.DataSource; 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 ZhouXY108 * @since 1.0.0 */ public class SimpleJdbcTemplate implements JdbcOperations { @Nonnull private final DataSource dataSource; public SimpleJdbcTemplate(@Nonnull DataSource dataSource) { AssertTools.checkNotNull(dataSource); this.dataSource = dataSource; } // #region - query /** {@inheritDoc} */ @Override public T query(String sql, Object[] params, ResultHandler resultHandler) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.query(conn, sql, params, resultHandler); } } /** {@inheritDoc} */ @Override public T query(String sql, ResultHandler resultHandler) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.query(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler); } } // #endregion // #region - queryList /** {@inheritDoc} */ @Override public List queryList(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.queryList(conn, sql, params, rowMapper); } } /** {@inheritDoc} */ @Override public List queryList(String sql, Object[] params, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.queryList(conn, sql, params, clazz); } } /** {@inheritDoc} */ @Override public List> queryList(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.queryList(conn, sql, params, RowMapper.HASH_MAP_MAPPER); } } /** {@inheritDoc} */ @Override public List queryList(String sql, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } } /** {@inheritDoc} */ @Override public List queryList(String sql, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.queryList(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } } /** {@inheritDoc} */ @Override public List> 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 /** {@inheritDoc} */ @Override public Optional queryFirst(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final T result = JdbcOperationSupport.queryFirst(conn, sql, params, rowMapper); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public Optional queryFirst(String sql, Object[] params, Class clazz) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final T result = JdbcOperationSupport.queryFirst(conn, sql, params, clazz); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public Optional> queryFirst(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Map result = JdbcOperationSupport .queryFirst(conn, sql, params, RowMapper.HASH_MAP_MAPPER); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public Optional queryFirstString(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final String result = JdbcOperationSupport.queryFirstString(conn, sql, params); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public OptionalInt queryFirstInt(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Integer result = JdbcOperationSupport.queryFirstInt(conn, sql, params); return OptionalTools.optionalOf(result); } } /** {@inheritDoc} */ @Override public OptionalLong queryFirstLong(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Long result = JdbcOperationSupport.queryFirstLong(conn, sql, params); return OptionalTools.optionalOf(result); } } /** {@inheritDoc} */ @Override public OptionalDouble queryFirstDouble(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Double result = JdbcOperationSupport.queryFirstDouble(conn, sql, params); return OptionalTools.optionalOf(result); } } /** {@inheritDoc} */ @Override public Optional queryFirstBigDecimal(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final BigDecimal result = JdbcOperationSupport.queryFirstBigDecimal(conn, sql, params); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public Optional queryFirst(String sql, RowMapper 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 Optional queryFirst(String sql, Class 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> queryFirst(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Map result = JdbcOperationSupport .queryFirst(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public Optional queryFirstString(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final String result = JdbcOperationSupport. queryFirstString(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public OptionalInt queryFirstInt(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Integer result = JdbcOperationSupport .queryFirstInt(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return OptionalTools.optionalOf(result); } } /** {@inheritDoc} */ @Override public OptionalLong queryFirstLong(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Long result = JdbcOperationSupport .queryFirstLong(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return OptionalTools.optionalOf(result); } } /** {@inheritDoc} */ @Override public OptionalDouble queryFirstDouble(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Double result = JdbcOperationSupport .queryFirstDouble(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return OptionalTools.optionalOf(result); } } /** {@inheritDoc} */ @Override public Optional queryFirstBigDecimal(String sql) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final BigDecimal result = JdbcOperationSupport .queryFirstBigDecimal(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return Optional.ofNullable(result); } } /** {@inheritDoc} */ @Override public boolean queryAsBoolean(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 queryAsBoolean(String sql, Object[] params) // TODO 单元测试 throws SQLException { try (Connection conn = this.dataSource.getConnection()) { final Boolean result = JdbcOperationSupport .queryFirstBoolean(conn, sql, params); return Boolean.TRUE.equals(result); } } // #endregion // #region - update & batchUpdate /** {@inheritDoc} */ @Override public int update(String sql, Object[] params) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.update(conn, sql, params); } } /** {@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 List update(String sql, Object[] params, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.update(conn, sql, params, rowMapper); } } /** {@inheritDoc} */ @Override public List update(String sql, RowMapper rowMapper) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.update(conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } } /** {@inheritDoc} */ @Override public List batchUpdate(String sql, @Nullable Collection params, int batchSize) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport.batchUpdate(conn, sql, params, batchSize); } } /** {@inheritDoc} */ @Override public List batchUpdateAndIgnoreException(String sql, @Nullable Collection params, int batchSize, List exceptions) throws SQLException { try (Connection conn = this.dataSource.getConnection()) { return JdbcOperationSupport .batchUpdateAndIgnoreException(conn, sql, params, batchSize, exceptions); } } // #endregion // #region - transaction /** * 执行事务。如果未发生异常,则提交事务;当有异常发生时,回滚事务 * *

* 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.accept(new JdbcExecutor(conn)); conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(autoCommit); } } } /** * 执行事务。 * 如果 {@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()) { final boolean autoCommit = conn.getAutoCommit(); try { conn.setAutoCommit(false); if (operations.test(new JdbcExecutor(conn))) { conn.commit(); } else { conn.rollback(); } } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(autoCommit); } } } // #endregion public static final class JdbcExecutor implements JdbcOperations { private final Connection conn; private JdbcExecutor(Connection conn) { this.conn = conn; } // #region - query /** {@inheritDoc} */ @Override public T query(String sql, Object[] params, ResultHandler resultHandler) throws SQLException { return JdbcOperationSupport.query(this.conn, sql, params, resultHandler); } /** {@inheritDoc} */ @Override public T query(String sql, ResultHandler resultHandler) throws SQLException { return JdbcOperationSupport .query(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, resultHandler); } // #endregion // #region - queryList /** {@inheritDoc} */ @Override public List queryList(String sql, Object[] params, RowMapper rowMapper) throws SQLException { return JdbcOperationSupport.queryList(this.conn, sql, params, rowMapper); } /** {@inheritDoc} */ @Override public List queryList(String sql, Object[] params, Class clazz) throws SQLException { return JdbcOperationSupport.queryList(this.conn, sql, params, clazz); } /** {@inheritDoc} */ @Override public List> queryList(String sql, Object[] params) throws SQLException { return JdbcOperationSupport.queryList(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); } /** {@inheritDoc} */ @Override public List queryList(String sql, RowMapper rowMapper) throws SQLException { return JdbcOperationSupport .queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } /** {@inheritDoc} */ @Override public List queryList(String sql, Class clazz) throws SQLException { return JdbcOperationSupport.queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); } /** {@inheritDoc} */ @Override public List> queryList(String sql) throws SQLException { return JdbcOperationSupport .queryList(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); } // #endregion // #region - queryFirst /** {@inheritDoc} */ @Override public Optional queryFirst(String sql, Object[] params, RowMapper rowMapper) throws SQLException { final T result = JdbcOperationSupport.queryFirst(this.conn, sql, params, rowMapper); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public Optional queryFirst(String sql, Object[] params, Class clazz) throws SQLException { final T result = JdbcOperationSupport.queryFirst(this.conn, sql, params, clazz); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public Optional> queryFirst(String sql, Object[] params) throws SQLException { final Map result = JdbcOperationSupport .queryFirst(this.conn, sql, params, RowMapper.HASH_MAP_MAPPER); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public Optional queryFirstString(String sql, Object[] params) throws SQLException { final String result = JdbcOperationSupport.queryFirstString(this.conn, sql, params); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public OptionalInt queryFirstInt(String sql, Object[] params) throws SQLException { final Integer result = JdbcOperationSupport.queryFirstInt(this.conn, sql, params); return OptionalTools.optionalOf(result); } /** {@inheritDoc} */ @Override public OptionalLong queryFirstLong(String sql, Object[] params) throws SQLException { final Long result = JdbcOperationSupport.queryFirstLong(this.conn, sql, params); return OptionalTools.optionalOf(result); } /** {@inheritDoc} */ @Override public OptionalDouble queryFirstDouble(String sql, Object[] params) throws SQLException { final Double result = JdbcOperationSupport.queryFirstDouble(this.conn, sql, params); return OptionalTools.optionalOf(result); } /** {@inheritDoc} */ @Override public Optional queryFirstBigDecimal(String sql, Object[] params) throws SQLException { final BigDecimal result = JdbcOperationSupport.queryFirstBigDecimal(this.conn, sql, params); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public Optional queryFirst(String sql, RowMapper rowMapper) throws SQLException { final T result = JdbcOperationSupport .queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public Optional queryFirst(String sql, Class clazz) throws SQLException { final T result = JdbcOperationSupport .queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, clazz); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public Optional> queryFirst(String sql) throws SQLException { final Map result = JdbcOperationSupport .queryFirst(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, RowMapper.HASH_MAP_MAPPER); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public Optional queryFirstString(String sql) throws SQLException { final String result = JdbcOperationSupport .queryFirstString(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public OptionalInt queryFirstInt(String sql) throws SQLException { final Integer result = JdbcOperationSupport .queryFirstInt(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return OptionalTools.optionalOf(result); } /** {@inheritDoc} */ @Override public OptionalLong queryFirstLong(String sql) throws SQLException { final Long result = JdbcOperationSupport .queryFirstLong(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return OptionalTools.optionalOf(result); } /** {@inheritDoc} */ @Override public OptionalDouble queryFirstDouble(String sql) throws SQLException { final Double result = JdbcOperationSupport .queryFirstDouble(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return OptionalTools.optionalOf(result); } /** {@inheritDoc} */ @Override public Optional queryFirstBigDecimal(String sql) throws SQLException { final BigDecimal result = JdbcOperationSupport .queryFirstBigDecimal(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY); return Optional.ofNullable(result); } /** {@inheritDoc} */ @Override public boolean queryAsBoolean(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 queryAsBoolean(String sql, Object[] params) throws SQLException { final Boolean result = JdbcOperationSupport.queryFirstBoolean(this.conn, sql, params); return Boolean.TRUE.equals(result); } // #endregion // #region - update & batchUpdate /** {@inheritDoc} */ @Override public int update(String sql, Object[] params) throws SQLException { 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 List update(String sql, Object[] params, RowMapper rowMapper) throws SQLException { return JdbcOperationSupport.update(this.conn, sql, params, rowMapper); } /** {@inheritDoc} */ @Override public List update(String sql, RowMapper rowMapper) throws SQLException { return JdbcOperationSupport.update(this.conn, sql, ParamBuilder.EMPTY_OBJECT_ARRAY, rowMapper); } /** {@inheritDoc} */ @Override public List batchUpdate(String sql, @Nullable Collection params, int batchSize) throws SQLException { return JdbcOperationSupport.batchUpdate(this.conn, sql, params, batchSize); } /** {@inheritDoc} */ @Override public List batchUpdateAndIgnoreException(String sql, @Nullable Collection params, int batchSize, List exceptions) throws SQLException { return JdbcOperationSupport .batchUpdateAndIgnoreException(this.conn, sql, params, batchSize, exceptions); } // #endregion } }