From 9bbfdca8203938e471e0b976abf4bef8e270642c Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 20 Apr 2023 01:08:19 +0800 Subject: [PATCH] fix code --- .../org/dromara/hutool/db/DaoTemplate.java | 4 +- .../org/dromara/hutool/db/DialectRunner.java | 6 +- .../org/dromara/hutool/db/StatementUtil.java | 253 ++++-------- .../dromara/hutool/db/sql/SqlExecutor.java | 6 +- .../org/dromara/hutool/db/sql/SqlLog.java | 5 +- .../hutool/db/sql/StatementBuilder.java | 198 +++++++++ .../hutool/db/sql/StatementWrapper.java | 387 ++++++++++++------ 7 files changed, 541 insertions(+), 318 deletions(-) create mode 100644 hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementBuilder.java diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/DaoTemplate.java b/hutool-db/src/main/java/org/dromara/hutool/db/DaoTemplate.java index 27867532e..f24c4dfb3 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/DaoTemplate.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/DaoTemplate.java @@ -23,8 +23,10 @@ import java.util.List; /** * 数据访问层模板
* 此模板用于简化对指定表的操作,简化的操作如下:
- * 1、在初始化时指定了表名,CRUD操作时便不需要表名
+ *
+ * 1、在初始化时指定了表名,CRUD操作时便不需要表名
  * 2、在初始化时指定了主键,某些需要主键的操作便不需要指定主键类型
+ * 
* * @author Looly */ diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/DialectRunner.java b/hutool-db/src/main/java/org/dromara/hutool/db/DialectRunner.java index 4488daf62..157993775 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/DialectRunner.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/DialectRunner.java @@ -22,11 +22,7 @@ import org.dromara.hutool.db.dialect.DialectFactory; import org.dromara.hutool.db.handler.NumberHandler; import org.dromara.hutool.db.handler.PageResultHandler; import org.dromara.hutool.db.handler.RsHandler; -import org.dromara.hutool.db.sql.Query; -import org.dromara.hutool.db.sql.SqlBuilder; -import org.dromara.hutool.db.sql.SqlExecutor; -import org.dromara.hutool.db.sql.SqlUtil; -import org.dromara.hutool.db.sql.QuoteWrapper; +import org.dromara.hutool.db.sql.*; import java.io.Serializable; import java.sql.Connection; diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java b/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java index b656bf8eb..c4e255d38 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java @@ -13,30 +13,16 @@ package org.dromara.hutool.db; import org.dromara.hutool.core.collection.iter.ArrayIter; -import org.dromara.hutool.core.convert.Convert; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.map.MapUtil; -import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.db.handler.ResultSetUtil; import org.dromara.hutool.db.handler.RsHandler; -import org.dromara.hutool.db.sql.NamedSql; import org.dromara.hutool.db.sql.SqlBuilder; import org.dromara.hutool.db.sql.SqlLog; -import org.dromara.hutool.db.sql.SqlUtil; +import org.dromara.hutool.db.sql.StatementBuilder; +import org.dromara.hutool.db.sql.StatementWrapper; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Types; +import java.sql.*; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -47,56 +33,6 @@ import java.util.Map; * @since 4.0.10 */ public class StatementUtil { - /** - * 填充SQL的参数。 - * - * @param ps PreparedStatement - * @param params SQL参数 - * @return {@link PreparedStatement} - * @throws SQLException SQL执行异常 - */ - public static PreparedStatement fillParams(final PreparedStatement ps, final Object... params) throws SQLException { - if (ArrayUtil.isEmpty(params)) { - return ps; - } - return fillParams(ps, new ArrayIter<>(params)); - } - - /** - * 填充SQL的参数。
- * 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理 - * - * @param ps PreparedStatement - * @param params SQL参数 - * @return {@link PreparedStatement} - * @throws SQLException SQL执行异常 - */ - public static PreparedStatement fillParams(final PreparedStatement ps, final Iterable params) throws SQLException { - return fillParams(ps, params, null); - } - - /** - * 填充SQL的参数。
- * 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理 - * - * @param ps PreparedStatement - * @param params SQL参数 - * @param nullTypeCache null参数的类型缓存,避免循环中重复获取类型 - * @return {@link PreparedStatement} - * @throws SQLException SQL执行异常 - * @since 4.6.7 - */ - public static PreparedStatement fillParams(final PreparedStatement ps, final Iterable params, final Map nullTypeCache) throws SQLException { - if (null == params) { - return ps;// 无参数 - } - - int paramIndex = 1;//第一个参数从1计数 - for (final Object param : params) { - setParam(ps, paramIndex++, param, nullTypeCache); - } - return ps; - } /** * 创建{@link PreparedStatement} @@ -135,26 +71,14 @@ public class StatementUtil { * @throws SQLException SQL异常 * @since 3.2.3 */ - public static PreparedStatement prepareStatement(final Connection conn, String sql, Object... params) throws SQLException { - Assert.notBlank(sql, "Sql String must be not blank!"); - sql = sql.trim(); - - if(ArrayUtil.isNotEmpty(params) && 1 == params.length && params[0] instanceof Map){ - // 检查参数是否为命名方式的参数 - final NamedSql namedSql = new NamedSql(sql, Convert.toMap(String.class, Object.class, params[0])); - sql = namedSql.getSql(); - params = namedSql.getParams(); - } - - SqlLog.INSTANCE.log(sql, ArrayUtil.isEmpty(params) ? null : params); - final PreparedStatement ps; - if (GlobalDbConfig.returnGeneratedKey && StrUtil.startWithIgnoreCase(sql, "insert")) { - // 插入默认返回主键 - ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - } else { - ps = conn.prepareStatement(sql); - } - return fillParams(ps, params); + public static PreparedStatement prepareStatement(final Connection conn, final String sql, final Object... params) throws SQLException { + return StatementBuilder.of() + .setConnection(conn) + .setReturnGeneratedKey(GlobalDbConfig.returnGeneratedKey) + .setSqlLog(SqlLog.INSTANCE) + .setSql(sql) + .setParams(params) + .build(); } /** @@ -181,18 +105,13 @@ public class StatementUtil { * @throws SQLException SQL异常 * @since 4.1.13 */ - public static PreparedStatement prepareStatementForBatch(final Connection conn, String sql, final Iterable paramsBatch) throws SQLException { - Assert.notBlank(sql, "Sql String must be not blank!"); - - sql = sql.trim(); - SqlLog.INSTANCE.log(sql, paramsBatch); - final PreparedStatement ps = conn.prepareStatement(sql); - final Map nullTypeMap = new HashMap<>(); - for (final Object[] params : paramsBatch) { - fillParams(ps, new ArrayIter<>(params), nullTypeMap); - ps.addBatch(); - } - return ps; + public static PreparedStatement prepareStatementForBatch(final Connection conn, final String sql, final Iterable paramsBatch) throws SQLException { + return StatementBuilder.of() + .setConnection(conn) + .setReturnGeneratedKey(GlobalDbConfig.returnGeneratedKey) + .setSqlLog(SqlLog.INSTANCE) + .setSql(sql) + .buildForBatch(paramsBatch); } /** @@ -206,19 +125,13 @@ public class StatementUtil { * @throws SQLException SQL异常 * @since 4.6.7 */ - public static PreparedStatement prepareStatementForBatch(final Connection conn, String sql, final Iterable fields, final Entity... entities) throws SQLException { - Assert.notBlank(sql, "Sql String must be not blank!"); - - sql = sql.trim(); - SqlLog.INSTANCE.logForBatch(sql); - final PreparedStatement ps = conn.prepareStatement(sql); - //null参数的类型缓存,避免循环中重复获取类型 - final Map nullTypeMap = new HashMap<>(); - for (final Entity entity : entities) { - fillParams(ps, MapUtil.valuesOfKeys(entity, fields), nullTypeMap); - ps.addBatch(); - } - return ps; + public static PreparedStatement prepareStatementForBatch(final Connection conn, final String sql, final Iterable fields, final Entity... entities) throws SQLException { + return StatementBuilder.of() + .setConnection(conn) + .setReturnGeneratedKey(GlobalDbConfig.returnGeneratedKey) + .setSqlLog(SqlLog.INSTANCE) + .setSql(sql) + .buildForBatch(fields, entities); } /** @@ -237,10 +150,12 @@ public class StatementUtil { sql = sql.trim(); SqlLog.INSTANCE.log(sql, params); final CallableStatement call = conn.prepareCall(sql); - fillParams(call, params); + fillArrayParam(call, params); return call; } + // region ----- getGeneratedKey + /** * 获得自增键的值
* 此方法对于Oracle无效(返回null) @@ -250,7 +165,7 @@ public class StatementUtil { * @throws SQLException SQL执行异常 */ public static Long getGeneratedKeyOfLong(final Statement ps) throws SQLException { - return getGeneratedKeys(ps, (rs)->{ + return getGeneratedKeys(ps, (rs) -> { Long generatedKey = null; if (rs != null && rs.next()) { try { @@ -276,9 +191,10 @@ public class StatementUtil { /** * 获取主键,并使用{@link RsHandler} 处理后返回 + * * @param statement {@link Statement} * @param rsHandler 主键结果集处理器 - * @param 自定义主键类型 + * @param 自定义主键类型 * @return 主键 * @throws SQLException SQL执行异常 * @since 5.5.3 @@ -288,6 +204,7 @@ public class StatementUtil { return rsHandler.handle(rs); } } + // endregion /** * 获取null字段对应位置的数据类型
@@ -300,19 +217,47 @@ public class StatementUtil { */ public static int getTypeOfNull(final PreparedStatement ps, final int paramIndex) { Assert.notNull(ps, "ps PreparedStatement must be not null in (getTypeOfNull)!"); + return StatementWrapper.of(ps).getTypeOfNull(paramIndex); + } - int sqlType = Types.VARCHAR; + /** + * 填充SQL的参数。 + * + * @param ps PreparedStatement + * @param params SQL参数 + * @return {@link PreparedStatement} + * @throws SQLException SQL执行异常 + */ + public static PreparedStatement fillArrayParam(final PreparedStatement ps, final Object... params) throws SQLException { + return StatementWrapper.of(ps).fillArrayParam(params); + } - final ParameterMetaData pmd; - try { - pmd = ps.getParameterMetaData(); - sqlType = pmd.getParameterType(paramIndex); - } catch (final SQLException ignore) { - // ignore - // log.warn("Null param of index [{}] type get failed, by: {}", paramIndex, e.getMessage()); - } + /** + * 填充SQL的参数。
+ * 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理 + * + * @param ps PreparedStatement + * @param params SQL参数 + * @return {@link PreparedStatement} + * @throws SQLException SQL执行异常 + */ + public static PreparedStatement fillParams(final PreparedStatement ps, final Iterable params) throws SQLException { + return fillParams(ps, params, null); + } - return sqlType; + /** + * 填充SQL的参数。
+ * 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理 + * + * @param ps PreparedStatement + * @param params SQL参数 + * @param nullTypeCache null参数的类型缓存,避免循环中重复获取类型 + * @return {@link PreparedStatement} + * @throws SQLException SQL执行异常 + * @since 4.6.7 + */ + public static PreparedStatement fillParams(final PreparedStatement ps, final Iterable params, final Map nullTypeCache) throws SQLException { + return StatementWrapper.of(ps).fillParams(params, nullTypeCache); } /** @@ -325,62 +270,6 @@ public class StatementUtil { * @since 4.6.7 */ public static void setParam(final PreparedStatement ps, final int paramIndex, final Object param) throws SQLException { - setParam(ps, paramIndex, param, null); + StatementWrapper.of(ps).setParam(paramIndex, param); } - - //--------------------------------------------------------------------------------------------- Private method start - - /** - * 为{@link PreparedStatement} 设置单个参数 - * - * @param ps {@link PreparedStatement} - * @param paramIndex 参数位置,从1开始 - * @param param 参数,不能为{@code null} - * @param nullTypeCache 用于缓存参数为null位置的类型,避免重复获取 - * @throws SQLException SQL异常 - * @since 4.6.7 - */ - private static void setParam(final PreparedStatement ps, final int paramIndex, final Object param, final Map nullTypeCache) throws SQLException { - if (null == param) { - Integer type = (null == nullTypeCache) ? null : nullTypeCache.get(paramIndex); - if (null == type) { - type = getTypeOfNull(ps, paramIndex); - if (null != nullTypeCache) { - nullTypeCache.put(paramIndex, type); - } - } - ps.setNull(paramIndex, type); - } - - // 日期特殊处理,默认按照时间戳传入,避免毫秒丢失 - if (param instanceof java.util.Date) { - if (param instanceof java.sql.Date) { - ps.setDate(paramIndex, (java.sql.Date) param); - } else if (param instanceof java.sql.Time) { - ps.setTime(paramIndex, (java.sql.Time) param); - } else { - ps.setTimestamp(paramIndex, SqlUtil.toSqlTimestamp((java.util.Date) param)); - } - return; - } - - // 针对大数字类型的特殊处理 - if (param instanceof Number) { - if (param instanceof BigDecimal) { - // BigDecimal的转换交给JDBC驱动处理 - ps.setBigDecimal(paramIndex, (BigDecimal) param); - return; - } - if (param instanceof BigInteger) { - // BigInteger转为BigDecimal - ps.setBigDecimal(paramIndex, new BigDecimal((BigInteger) param)); - return; - } - // 忽略其它数字类型,按照默认类型传入 - } - - // 其它参数类型 - ps.setObject(paramIndex, param); - } - //--------------------------------------------------------------------------------------------- Private method end } diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlExecutor.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlExecutor.java index 8b300df76..85712201a 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlExecutor.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlExecutor.java @@ -322,7 +322,7 @@ public class SqlExecutor { */ public static int executeUpdate(final PreparedStatement ps, final Object... params) throws DbRuntimeException { try { - StatementUtil.fillParams(ps, params); + StatementUtil.fillArrayParam(ps, params); return ps.executeUpdate(); } catch (final SQLException e) { throw new DbRuntimeException(e); @@ -341,7 +341,7 @@ public class SqlExecutor { */ public static boolean execute(final PreparedStatement ps, final Object... params) throws DbRuntimeException { try { - StatementUtil.fillParams(ps, params); + StatementUtil.fillArrayParam(ps, params); return ps.execute(); } catch (final SQLException e) { throw new DbRuntimeException(e); @@ -361,7 +361,7 @@ public class SqlExecutor { */ public static T query(final PreparedStatement ps, final RsHandler rsh, final Object... params) throws DbRuntimeException { try { - StatementUtil.fillParams(ps, params); + StatementUtil.fillArrayParam(ps, params); return executeQuery(ps, rsh); } catch (final SQLException e) { throw new DbRuntimeException(e); diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlLog.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlLog.java index 640e25faa..0d87a404a 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlLog.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/SqlLog.java @@ -22,11 +22,12 @@ import org.dromara.hutool.log.level.Level; * @author looly * @since 4.1.0 */ -public enum SqlLog { +public class SqlLog { + /** * 单例 */ - INSTANCE; + public static SqlLog INSTANCE = new SqlLog(); private final static Log log = LogFactory.get(); diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementBuilder.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementBuilder.java new file mode 100644 index 000000000..4dc2b63b1 --- /dev/null +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementBuilder.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.db.sql; + +import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.collection.iter.ArrayIter; +import org.dromara.hutool.core.convert.Convert; +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.lang.builder.Builder; +import org.dromara.hutool.core.map.MapUtil; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.db.DbRuntimeException; +import org.dromara.hutool.db.Entity; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link PreparedStatement}构建器,构建结果为{@link StatementWrapper} + * + * @author looly + * @since 6.0.0 + */ +public class StatementBuilder implements Builder { + private static final long serialVersionUID = 1L; + + /** + * 创建构建器 + * + * @return StatementBuilder + */ + public static StatementBuilder of() { + return new StatementBuilder(); + } + + private SqlLog sqlLog; + private Connection connection; + private String sql; + private Object[] params; + private boolean returnGeneratedKey = true; + + /** + * 设置SQL日志 + * @param sqlLog {@link SqlLog} + * @return this + */ + public StatementBuilder setSqlLog(final SqlLog sqlLog) { + this.sqlLog = sqlLog; + return this; + } + + /** + * 设置连接 + * + * @param connection {@link Connection} + * @return this + */ + public StatementBuilder setConnection(final Connection connection) { + this.connection = connection; + return this; + } + + /** + * 设置执行的SQL语句 + * + * @param sql SQL语句 + * @return this + */ + public StatementBuilder setSql(final String sql) { + this.sql = StrUtil.trim(sql); + return this; + } + + /** + * 设置SQL的"?"对应的参数 + * + * @param params 参数数组 + * @return this + */ + public StatementBuilder setParams(final Object... params) { + this.params = params; + return this; + } + + /** + * 设置是否返回主键 + * @param returnGeneratedKey 是否返回主键 + * @return this + */ + public StatementBuilder setReturnGeneratedKey(final boolean returnGeneratedKey) { + this.returnGeneratedKey = returnGeneratedKey; + return this; + } + + @Override + public StatementWrapper build() { + try { + return _build(); + } catch (final SQLException e) { + throw new DbRuntimeException(e); + } + } + + /** + * 创建批量操作的{@link StatementWrapper} + * + * @param paramsBatch "?"对应参数批次列表 + * @return {@link StatementWrapper} + * @throws DbRuntimeException SQL异常 + */ + public StatementWrapper buildForBatch(final Iterable paramsBatch) throws DbRuntimeException { + Assert.notBlank(sql, "Sql String must be not blank!"); + + sqlLog.log(sql, paramsBatch); + + final StatementWrapper ps; + try { + ps = StatementWrapper.of(connection.prepareStatement(sql)); + final Map nullTypeMap = new HashMap<>(); + for (final Object[] params : paramsBatch) { + ps.fillParams(new ArrayIter<>(params), nullTypeMap); + ps.addBatch(); + } + } catch (final SQLException e) { + throw new DbRuntimeException(e); + } + return ps; + } + + /** + * 创建批量操作的{@link StatementWrapper} + * + * @param fields 字段列表,用于获取对应值 + * @param entities "?"对应参数批次列表 + * @return {@link StatementWrapper} + * @throws DbRuntimeException SQL异常 + */ + public StatementWrapper buildForBatch(final Iterable fields, final Entity... entities) throws DbRuntimeException { + Assert.notBlank(sql, "Sql String must be not blank!"); + + sqlLog.logForBatch(sql); + + final StatementWrapper ps; + try { + ps = StatementWrapper.of(connection.prepareStatement(sql)); + final Map nullTypeMap = new HashMap<>(); + for (final Entity entity : entities) { + ps.fillParams(MapUtil.valuesOfKeys(entity, fields), nullTypeMap); + ps.addBatch(); + } + } catch (final SQLException e) { + throw new DbRuntimeException(e); + } + return ps; + } + + /** + * 构建{@link StatementWrapper} + * + * @return {@link StatementWrapper} + * @throws SQLException SQL异常 + */ + private StatementWrapper _build() throws SQLException { + Assert.notBlank(sql, "Sql String must be not blank!"); + + if (ArrayUtil.isNotEmpty(params) && 1 == params.length && params[0] instanceof Map) { + // 检查参数是否为命名方式的参数 + final NamedSql namedSql = new NamedSql(sql, Convert.toMap(String.class, Object.class, params[0])); + sql = namedSql.getSql(); + params = namedSql.getParams(); + } + + sqlLog.log(sql, ArrayUtil.isEmpty(params) ? null : params); + final PreparedStatement ps; + if (returnGeneratedKey && StrUtil.startWithIgnoreCase(sql, "insert")) { + // 插入默认返回主键 + ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + } else { + ps = connection.prepareStatement(sql); + } + + return StatementWrapper.of(ps).fillArrayParam(params); + } +} diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementWrapper.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementWrapper.java index 01078f4ed..164670baf 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementWrapper.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/StatementWrapper.java @@ -12,30 +12,19 @@ package org.dromara.hutool.db.sql; -import org.dromara.hutool.core.func.Wrapper; +import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.collection.iter.ArrayIter; +import org.dromara.hutool.core.func.SimpleWrapper; +import org.dromara.hutool.core.lang.Assert; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.Date; -import java.sql.NClob; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.RowId; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Time; -import java.sql.Timestamp; +import java.sql.*; import java.util.Calendar; +import java.util.Map; /** * {@link PreparedStatement} 包装类,用于添加拦截方法功能
@@ -49,517 +38,665 @@ import java.util.Calendar; * @author looly * @since 4.1.0 */ -public class StatementWrapper implements PreparedStatement, Wrapper { +public class StatementWrapper extends SimpleWrapper implements PreparedStatement { - private final PreparedStatement rawStatement; + /** + * 构建StatementWrapper + * + * @param raw {@link PreparedStatement} + * @return StatementWrapper + */ + public static StatementWrapper of(final PreparedStatement raw) { + return raw instanceof StatementWrapper ? (StatementWrapper) raw : + new StatementWrapper(raw); + } /** * 构造 * - * @param rawStatement {@link PreparedStatement} + * @param raw {@link PreparedStatement} */ - public StatementWrapper(final PreparedStatement rawStatement) { - this.rawStatement = rawStatement; + public StatementWrapper(final PreparedStatement raw) { + super(raw); } + // region ----- override methods @Override public ResultSet executeQuery(final String sql) throws SQLException { - return rawStatement.executeQuery(sql); + return this.raw.executeQuery(sql); } @Override public int executeUpdate(final String sql) throws SQLException { - return rawStatement.executeUpdate(sql); + return this.raw.executeUpdate(sql); } @Override public void close() throws SQLException { - rawStatement.close(); + this.raw.close(); } @Override public int getMaxFieldSize() throws SQLException { - return rawStatement.getMaxFieldSize(); + return this.raw.getMaxFieldSize(); } @Override public void setMaxFieldSize(final int max) throws SQLException { - rawStatement.setMaxFieldSize(max); + this.raw.setMaxFieldSize(max); } @Override public int getMaxRows() throws SQLException { - return rawStatement.getMaxRows(); + return this.raw.getMaxRows(); } @Override public void setMaxRows(final int max) throws SQLException { - rawStatement.setMaxRows(max); + this.raw.setMaxRows(max); } @Override public void setEscapeProcessing(final boolean enable) throws SQLException { - rawStatement.setEscapeProcessing(enable); + this.raw.setEscapeProcessing(enable); } @Override public int getQueryTimeout() throws SQLException { - return rawStatement.getQueryTimeout(); + return this.raw.getQueryTimeout(); } @Override public void setQueryTimeout(final int seconds) throws SQLException { - rawStatement.setQueryTimeout(seconds); + this.raw.setQueryTimeout(seconds); } @Override public void cancel() throws SQLException { - rawStatement.cancel(); + this.raw.cancel(); } @Override public SQLWarning getWarnings() throws SQLException { - return rawStatement.getWarnings(); + return this.raw.getWarnings(); } @Override public void clearWarnings() throws SQLException { - rawStatement.clearWarnings(); + this.raw.clearWarnings(); } @Override public void setCursorName(final String name) throws SQLException { - rawStatement.setCursorName(name); + this.raw.setCursorName(name); } @Override public boolean execute(final String sql) throws SQLException { - return rawStatement.execute(sql); + return this.raw.execute(sql); } @Override public ResultSet getResultSet() throws SQLException { - return rawStatement.getResultSet(); + return this.raw.getResultSet(); } @Override public int getUpdateCount() throws SQLException { - return rawStatement.getUpdateCount(); + return this.raw.getUpdateCount(); } @Override public boolean getMoreResults() throws SQLException { - return rawStatement.getMoreResults(); + return this.raw.getMoreResults(); } @Override public void setFetchDirection(final int direction) throws SQLException { - rawStatement.setFetchDirection(direction); + this.raw.setFetchDirection(direction); } @Override public int getFetchDirection() throws SQLException { - return rawStatement.getFetchDirection(); + return this.raw.getFetchDirection(); } @Override public void setFetchSize(final int rows) throws SQLException { - rawStatement.setFetchSize(rows); + this.raw.setFetchSize(rows); } @Override public int getFetchSize() throws SQLException { - return rawStatement.getFetchSize(); + return this.raw.getFetchSize(); } @Override public int getResultSetConcurrency() throws SQLException { - return rawStatement.getResultSetConcurrency(); + return this.raw.getResultSetConcurrency(); } @Override public int getResultSetType() throws SQLException { - return rawStatement.getResultSetType(); + return this.raw.getResultSetType(); } @Override public void addBatch(final String sql) throws SQLException { - rawStatement.addBatch(sql); + this.raw.addBatch(sql); } @Override public void clearBatch() throws SQLException { - rawStatement.clearBatch(); + this.raw.clearBatch(); } @Override public int[] executeBatch() throws SQLException { - return rawStatement.executeBatch(); + return this.raw.executeBatch(); } @Override public Connection getConnection() throws SQLException { - return rawStatement.getConnection(); + return this.raw.getConnection(); } @Override public boolean getMoreResults(final int current) throws SQLException { - return rawStatement.getMoreResults(current); + return this.raw.getMoreResults(current); } @Override public ResultSet getGeneratedKeys() throws SQLException { - return rawStatement.getGeneratedKeys(); + return this.raw.getGeneratedKeys(); } @Override public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { - return rawStatement.executeUpdate(sql, autoGeneratedKeys); + return this.raw.executeUpdate(sql, autoGeneratedKeys); } @Override public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException { - return rawStatement.executeUpdate(sql, columnIndexes); + return this.raw.executeUpdate(sql, columnIndexes); } @Override public int executeUpdate(final String sql, final String[] columnNames) throws SQLException { - return rawStatement.executeUpdate(sql, columnNames); + return this.raw.executeUpdate(sql, columnNames); } @Override public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException { - return rawStatement.execute(sql, autoGeneratedKeys); + return this.raw.execute(sql, autoGeneratedKeys); } @Override public boolean execute(final String sql, final int[] columnIndexes) throws SQLException { - return rawStatement.execute(sql, columnIndexes); + return this.raw.execute(sql, columnIndexes); } @Override public boolean execute(final String sql, final String[] columnNames) throws SQLException { - return rawStatement.execute(sql, columnNames); + return this.raw.execute(sql, columnNames); } @Override public int getResultSetHoldability() throws SQLException { - return rawStatement.getResultSetHoldability(); + return this.raw.getResultSetHoldability(); } @Override public boolean isClosed() throws SQLException { - return rawStatement.isClosed(); + return this.raw.isClosed(); } @Override public void setPoolable(final boolean poolable) throws SQLException { - rawStatement.setPoolable(poolable); + this.raw.setPoolable(poolable); } @Override public boolean isPoolable() throws SQLException { - return rawStatement.isPoolable(); + return this.raw.isPoolable(); } @Override public void closeOnCompletion() throws SQLException { - rawStatement.closeOnCompletion(); + this.raw.closeOnCompletion(); } @Override public boolean isCloseOnCompletion() throws SQLException { - return rawStatement.isCloseOnCompletion(); + return this.raw.isCloseOnCompletion(); } @Override public T unwrap(final Class iface) throws SQLException { - return rawStatement.unwrap(iface); + return this.raw.unwrap(iface); } @Override public boolean isWrapperFor(final Class iface) throws SQLException { - return rawStatement.isWrapperFor(iface); + return this.raw.isWrapperFor(iface); } @Override public ResultSet executeQuery() throws SQLException { - return rawStatement.executeQuery(); + return this.raw.executeQuery(); } @Override public int executeUpdate() throws SQLException { - return rawStatement.executeUpdate(); + return this.raw.executeUpdate(); } @Override public void setNull(final int parameterIndex, final int sqlType) throws SQLException { - rawStatement.setNull(parameterIndex, sqlType); + this.raw.setNull(parameterIndex, sqlType); } @Override public void setBoolean(final int parameterIndex, final boolean x) throws SQLException { - rawStatement.setBoolean(parameterIndex, x); + this.raw.setBoolean(parameterIndex, x); } @Override public void setByte(final int parameterIndex, final byte x) throws SQLException { - rawStatement.setByte(parameterIndex, x); + this.raw.setByte(parameterIndex, x); } @Override public void setShort(final int parameterIndex, final short x) throws SQLException { - rawStatement.setShort(parameterIndex, x); + this.raw.setShort(parameterIndex, x); } @Override public void setInt(final int parameterIndex, final int x) throws SQLException { - rawStatement.setInt(parameterIndex, x); + this.raw.setInt(parameterIndex, x); } @Override public void setLong(final int parameterIndex, final long x) throws SQLException { - rawStatement.setLong(parameterIndex, x); + this.raw.setLong(parameterIndex, x); } @Override public void setFloat(final int parameterIndex, final float x) throws SQLException { - rawStatement.setFloat(parameterIndex, x); + this.raw.setFloat(parameterIndex, x); } @Override public void setDouble(final int parameterIndex, final double x) throws SQLException { - rawStatement.setDouble(parameterIndex, x); + this.raw.setDouble(parameterIndex, x); } @Override public void setBigDecimal(final int parameterIndex, final BigDecimal x) throws SQLException { - rawStatement.setBigDecimal(parameterIndex, x); + this.raw.setBigDecimal(parameterIndex, x); } @Override public void setString(final int parameterIndex, final String x) throws SQLException { - rawStatement.setString(parameterIndex, x); + this.raw.setString(parameterIndex, x); } @Override public void setBytes(final int parameterIndex, final byte[] x) throws SQLException { - rawStatement.setBytes(parameterIndex, x); + this.raw.setBytes(parameterIndex, x); } @Override public void setDate(final int parameterIndex, final Date x) throws SQLException { - rawStatement.setDate(parameterIndex, x); + this.raw.setDate(parameterIndex, x); } @Override public void setTime(final int parameterIndex, final Time x) throws SQLException { - rawStatement.setTime(parameterIndex, x); + this.raw.setTime(parameterIndex, x); } @Override public void setTimestamp(final int parameterIndex, final Timestamp x) throws SQLException { - rawStatement.setTimestamp(parameterIndex, x); + this.raw.setTimestamp(parameterIndex, x); } @Override public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { - rawStatement.setAsciiStream(parameterIndex, x, length); + this.raw.setAsciiStream(parameterIndex, x, length); } @Override @Deprecated public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { - rawStatement.setUnicodeStream(parameterIndex, x, length); + this.raw.setUnicodeStream(parameterIndex, x, length); } @Override public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { - rawStatement.setBinaryStream(parameterIndex, x, length); + this.raw.setBinaryStream(parameterIndex, x, length); } @Override public void clearParameters() throws SQLException { - rawStatement.clearParameters(); + this.raw.clearParameters(); } @Override public void setObject(final int parameterIndex, final Object x, final int targetSqlType) throws SQLException { - rawStatement.setObject(parameterIndex, x, targetSqlType, targetSqlType); + this.raw.setObject(parameterIndex, x, targetSqlType, targetSqlType); } @Override public void setObject(final int parameterIndex, final Object x) throws SQLException { - rawStatement.setObject(parameterIndex, x); + this.raw.setObject(parameterIndex, x); } @Override public boolean execute() throws SQLException { - return rawStatement.execute(); + return this.raw.execute(); } @Override public void addBatch() throws SQLException { - rawStatement.addBatch(); + this.raw.addBatch(); } @Override public void setCharacterStream(final int parameterIndex, final Reader reader, final int length) throws SQLException { - rawStatement.setCharacterStream(parameterIndex, reader, length); + this.raw.setCharacterStream(parameterIndex, reader, length); } @Override public void setRef(final int parameterIndex, final Ref x) throws SQLException { - rawStatement.setRef(parameterIndex, x); + this.raw.setRef(parameterIndex, x); } @Override public void setBlob(final int parameterIndex, final Blob x) throws SQLException { - rawStatement.setBlob(parameterIndex, x); + this.raw.setBlob(parameterIndex, x); } @Override public void setClob(final int parameterIndex, final Clob x) throws SQLException { - rawStatement.setClob(parameterIndex, x); + this.raw.setClob(parameterIndex, x); } @Override public void setArray(final int parameterIndex, final Array x) throws SQLException { - rawStatement.setArray(parameterIndex, x); + this.raw.setArray(parameterIndex, x); } @Override public ResultSetMetaData getMetaData() throws SQLException { - return rawStatement.getMetaData(); + return this.raw.getMetaData(); } @Override public void setDate(final int parameterIndex, final Date x, final Calendar cal) throws SQLException { - rawStatement.setDate(parameterIndex, x, cal); + this.raw.setDate(parameterIndex, x, cal); } @Override public void setTime(final int parameterIndex, final Time x, final Calendar cal) throws SQLException { - rawStatement.setTime(parameterIndex, x, cal); + this.raw.setTime(parameterIndex, x, cal); } @Override public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) throws SQLException { - rawStatement.setTimestamp(parameterIndex, x, cal); + this.raw.setTimestamp(parameterIndex, x, cal); } @Override public void setNull(final int parameterIndex, final int sqlType, final String typeName) throws SQLException { - rawStatement.setNull(parameterIndex, sqlType, typeName); + this.raw.setNull(parameterIndex, sqlType, typeName); } @Override public void setURL(final int parameterIndex, final URL x) throws SQLException { - rawStatement.setURL(parameterIndex, x); + this.raw.setURL(parameterIndex, x); } @Override public ParameterMetaData getParameterMetaData() throws SQLException { - return rawStatement.getParameterMetaData(); + return this.raw.getParameterMetaData(); } @Override public void setRowId(final int parameterIndex, final RowId x) throws SQLException { - rawStatement.setRowId(parameterIndex, x); + this.raw.setRowId(parameterIndex, x); } @Override public void setNString(final int parameterIndex, final String value) throws SQLException { - rawStatement.setNString(parameterIndex, value); + this.raw.setNString(parameterIndex, value); } @Override public void setNCharacterStream(final int parameterIndex, final Reader value, final long length) throws SQLException { - rawStatement.setCharacterStream(parameterIndex, value, length); + this.raw.setCharacterStream(parameterIndex, value, length); } @Override public void setNClob(final int parameterIndex, final NClob value) throws SQLException { - rawStatement.setNClob(parameterIndex, value); + this.raw.setNClob(parameterIndex, value); } @Override public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { - rawStatement.setClob(parameterIndex, reader, length); + this.raw.setClob(parameterIndex, reader, length); } @Override public void setBlob(final int parameterIndex, final InputStream inputStream, final long length) throws SQLException { - rawStatement.setBlob(parameterIndex, inputStream, length); + this.raw.setBlob(parameterIndex, inputStream, length); } @Override public void setNClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { - rawStatement.setNClob(parameterIndex, reader, length); + this.raw.setNClob(parameterIndex, reader, length); } @Override public void setSQLXML(final int parameterIndex, final SQLXML xmlObject) throws SQLException { - rawStatement.setSQLXML(parameterIndex, xmlObject); + this.raw.setSQLXML(parameterIndex, xmlObject); } @Override public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scaleOrLength) throws SQLException { - rawStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + this.raw.setObject(parameterIndex, x, targetSqlType, scaleOrLength); } @Override public void setAsciiStream(final int parameterIndex, final InputStream x, final long length) throws SQLException { - rawStatement.setAsciiStream(parameterIndex, x, length); + this.raw.setAsciiStream(parameterIndex, x, length); } @Override public void setBinaryStream(final int parameterIndex, final InputStream x, final long length) throws SQLException { - rawStatement.setBinaryStream(parameterIndex, x, length); + this.raw.setBinaryStream(parameterIndex, x, length); } @Override public void setCharacterStream(final int parameterIndex, final Reader reader, final long length) throws SQLException { - rawStatement.setCharacterStream(parameterIndex, reader, length); + this.raw.setCharacterStream(parameterIndex, reader, length); } @Override public void setAsciiStream(final int parameterIndex, final InputStream x) throws SQLException { - rawStatement.setAsciiStream(parameterIndex, x); + this.raw.setAsciiStream(parameterIndex, x); } @Override public void setBinaryStream(final int parameterIndex, final InputStream x) throws SQLException { - rawStatement.setBinaryStream(parameterIndex, x); + this.raw.setBinaryStream(parameterIndex, x); } @Override public void setCharacterStream(final int parameterIndex, final Reader reader) throws SQLException { - rawStatement.setCharacterStream(parameterIndex, reader); + this.raw.setCharacterStream(parameterIndex, reader); } @Override public void setNCharacterStream(final int parameterIndex, final Reader value) throws SQLException { - rawStatement.setNCharacterStream(parameterIndex, value); + this.raw.setNCharacterStream(parameterIndex, value); } @Override public void setClob(final int parameterIndex, final Reader reader) throws SQLException { - rawStatement.setClob(parameterIndex, reader); + this.raw.setClob(parameterIndex, reader); } @Override public void setBlob(final int parameterIndex, final InputStream inputStream) throws SQLException { - rawStatement.setBlob(parameterIndex, inputStream); + this.raw.setBlob(parameterIndex, inputStream); } @Override public void setNClob(final int parameterIndex, final Reader reader) throws SQLException { - rawStatement.setNClob(parameterIndex, reader); + this.raw.setNClob(parameterIndex, reader); } - @Override - public PreparedStatement getRaw() { - return rawStatement; + // endregion + + // region ----- setParam + + /** + * 填充数组类型的SQL的参数。 + * + * @param params SQL参数 + * @return this + * @throws SQLException SQL执行异常 + */ + public StatementWrapper fillArrayParam(final Object... params) throws SQLException { + if (ArrayUtil.isEmpty(params)) { + return this; + } + return fillParams(new ArrayIter<>(params)); + } + + /** + * 填充SQL的参数。
+ * 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理 + * + * @param params SQL参数 + * @return this + * @throws SQLException SQL执行异常 + */ + public StatementWrapper fillParams(final Iterable params) throws SQLException { + return fillParams(params, null); + } + + /** + * 填充SQL的参数。
+ * 对于日期对象特殊处理:传入java.util.Date默认按照Timestamp处理 + * + * @param params SQL参数 + * @param nullTypeCache null参数的类型缓存,避免循环中重复获取类型 + * @return this + * @throws SQLException SQL执行异常 + * @since 4.6.7 + */ + public StatementWrapper fillParams(final Iterable params, final Map nullTypeCache) throws SQLException { + if (null == params) { + return this;// 无参数 + } + + int paramIndex = 1;//第一个参数从1计数 + for (final Object param : params) { + setParam(paramIndex++, param, nullTypeCache); + } + return this; + } + + /** + * 为{@link PreparedStatement} 设置单个参数 + * + * @param paramIndex 参数位置,从1开始 + * @param param 参数 + * @throws SQLException SQL异常 + * @since 4.6.7 + */ + public void setParam(final int paramIndex, final Object param) throws SQLException { + setParam(paramIndex, param, null); + } + + // endregion + + /** + * 获取null字段对应位置的数据类型
+ * 有些数据库对于null字段必须指定类型,否则会插入报错,此方法用于获取其类型,如果获取失败,使用默认的{@link Types#VARCHAR} + * + * @param paramIndex 参数位置,第一位从1开始 + * @return 数据类型,默认{@link Types#VARCHAR} + * @since 4.6.7 + */ + public int getTypeOfNull(final int paramIndex) { + Assert.notNull(this.raw, "ps PreparedStatement must be not null in (getTypeOfNull)!"); + + int sqlType = Types.VARCHAR; + + final ParameterMetaData pmd; + try { + pmd = this.raw.getParameterMetaData(); + sqlType = pmd.getParameterType(paramIndex); + } catch (final SQLException ignore) { + // ignore + // log.warn("Null param of index [{}] type get failed, by: {}", paramIndex, e.getMessage()); + } + + return sqlType; + } + + /** + * 为{@link PreparedStatement} 设置单个参数 + * + * @param paramIndex 参数位置,从1开始 + * @param param 参数,不能为{@code null} + * @param nullTypeCache 用于缓存参数为null位置的类型,避免重复获取 + * @throws SQLException SQL异常 + * @since 4.6.7 + */ + private void setParam(final int paramIndex, final Object param, final Map nullTypeCache) throws SQLException { + if (null == param) { + Integer type = (null == nullTypeCache) ? null : nullTypeCache.get(paramIndex); + if (null == type) { + type = getTypeOfNull(paramIndex); + if (null != nullTypeCache) { + nullTypeCache.put(paramIndex, type); + } + } + this.raw.setNull(paramIndex, type); + } + + // 日期特殊处理,默认按照时间戳传入,避免毫秒丢失 + if (param instanceof java.util.Date) { + if (param instanceof java.sql.Date) { + this.raw.setDate(paramIndex, (java.sql.Date) param); + } else if (param instanceof java.sql.Time) { + this.raw.setTime(paramIndex, (java.sql.Time) param); + } else { + this.raw.setTimestamp(paramIndex, SqlUtil.toSqlTimestamp((java.util.Date) param)); + } + return; + } + + // 针对大数字类型的特殊处理 + if (param instanceof Number) { + if (param instanceof BigDecimal) { + // BigDecimal的转换交给JDBC驱动处理 + this.raw.setBigDecimal(paramIndex, (BigDecimal) param); + return; + } + if (param instanceof BigInteger) { + // BigInteger转为BigDecimal + this.raw.setBigDecimal(paramIndex, new BigDecimal((BigInteger) param)); + return; + } + // 忽略其它数字类型,按照默认类型传入 + } + + // 其它参数类型 + this.raw.setObject(paramIndex, param); } }