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 68a32f881..679faed72 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 @@ -12,14 +12,15 @@ package org.dromara.hutool.db; +import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.collection.iter.ArrayIter; import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.db.handler.ResultSetUtil; import org.dromara.hutool.db.handler.RsHandler; import org.dromara.hutool.db.sql.SqlBuilder; -import org.dromara.hutool.db.sql.SqlLog; import org.dromara.hutool.db.sql.StatementBuilder; import org.dromara.hutool.db.sql.StatementWrapper; +import org.dromara.hutool.db.sql.filter.SqlLogFilter; import java.sql.*; import java.util.Collection; @@ -87,7 +88,7 @@ public class StatementUtil { return StatementBuilder.of() .setConnection(conn) .setReturnGeneratedKey(returnGeneratedKey) - .setSqlLog(SqlLog.INSTANCE) + .setSqlFilter(SqlLogFilter.INSTANCE) .setSql(sql) .setParams(params) .build(); @@ -120,29 +121,10 @@ public class StatementUtil { return StatementBuilder.of() .setConnection(conn) .setReturnGeneratedKey(false) - .setSqlLog(SqlLog.INSTANCE) + .setSqlFilter(SqlLogFilter.INSTANCE) .setSql(sql) - .buildForBatch(paramsBatch); - } - - /** - * 创建批量操作的{@link PreparedStatement} - * - * @param conn 数据库连接 - * @param sql SQL语句,使用"?"做为占位符 - * @param fields 字段列表,用于获取对应值 - * @param entities "?"对应参数批次列表 - * @return {@link PreparedStatement} - * @since 4.6.7 - */ - public static PreparedStatement prepareStatementForBatch(final Connection conn, final String sql, - final Iterable fields, final Entity... entities) { - return StatementBuilder.of() - .setConnection(conn) - .setReturnGeneratedKey(false) - .setSqlLog(SqlLog.INSTANCE) - .setSql(sql) - .buildForBatch(fields, entities); + .setParams(ArrayUtil.ofArray(paramsBatch, Object.class)) + .buildForBatch(); } /** @@ -158,7 +140,7 @@ public class StatementUtil { public static CallableStatement prepareCall(final Connection conn, final String sql, final Object... params) throws SQLException { return StatementBuilder.of() .setConnection(conn) - .setSqlLog(SqlLog.INSTANCE) + .setSqlFilter(SqlLogFilter.INSTANCE) .setSql(sql) .setParams(params) .buildForCall(); diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java index fdc6ff605..567307a31 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java @@ -53,21 +53,21 @@ public class AnsiSqlDialect implements Dialect { } @Override - public PreparedStatement psForInsert(final Connection conn, final Entity entity) throws SQLException { + public PreparedStatement psForInsert(final Connection conn, final Entity entity) { final SqlBuilder insert = SqlBuilder.of(quoteWrapper).insert(entity, this.dialectName()); return StatementUtil.prepareStatement(conn, insert); } @Override - public PreparedStatement psForInsertBatch(final Connection conn, final Entity... entities) throws SQLException { + public PreparedStatement psForInsertBatch(final Connection conn, final Entity... entities) { if (ArrayUtil.isEmpty(entities)) { throw new DbRuntimeException("Entities for batch insert is empty !"); } // 批量,根据第一行数据结构生成SQL占位符 final SqlBuilder insert = SqlBuilder.of(quoteWrapper).insert(entities[0], this.dialectName()); final Set fields = CollUtil.remove(entities[0].keySet(), StrUtil::isBlank); - return StatementUtil.prepareStatementForBatch(conn, insert.build(), fields, entities); + return StatementUtil.prepareStatementForBatch(conn, insert.build(), entities); } @Override @@ -116,7 +116,7 @@ public class AnsiSqlDialect implements Dialect { } @Override - public PreparedStatement psForPage(final Connection conn, SqlBuilder sqlBuilder, final Page page) throws SQLException { + public PreparedStatement psForPage(final Connection conn, SqlBuilder sqlBuilder, final Page page) { // 根据不同数据库在查询SQL语句基础上包装其分页的语句 if (null != page) { sqlBuilder = wrapPageSql(sqlBuilder.orderBy(page.getOrders()), page); diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java b/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java index 20cc1aa0b..90cef3077 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java @@ -19,6 +19,7 @@ import org.dromara.hutool.core.map.SafeConcurrentHashMap; import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.db.DbRuntimeException; +import org.dromara.hutool.db.DbUtil; import org.dromara.hutool.db.GlobalDbConfig; import org.dromara.hutool.db.driver.DriverUtil; import org.dromara.hutool.log.LogUtil; @@ -89,6 +90,7 @@ public class DSPool implements Closeable { */ public DSPool(final Setting setting, final DSFactory factory) { this.setting = null != setting ? setting : GlobalDbConfig.createDbSetting(); + DbUtil.setShowSqlGlobal(this.setting); this.factory = null != factory ? factory : SpiUtil.loadFirstAvailable(DSFactory.class); this.pool = new SafeConcurrentHashMap<>(); } diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/BoundSql.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/BoundSql.java index bf063e57e..1801c08c7 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/sql/BoundSql.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/BoundSql.java @@ -12,6 +12,7 @@ package org.dromara.hutool.db.sql; +import java.util.ArrayList; import java.util.List; /** @@ -22,8 +23,13 @@ import java.util.List; */ public class BoundSql { - protected String sql; - protected final List params; + private String sql; + private List params; + + /** + * 构造 + */ + public BoundSql() {} /** * 构造 @@ -45,6 +51,17 @@ public class BoundSql { return this.sql; } + /** + * 设置SQL语句 + * + * @param sql SQL语句 + * @return this + */ + public BoundSql setSql(final String sql) { + this.sql = sql; + return this; + } + /** * 获取参数列表,按照占位符顺序 * @@ -62,4 +79,29 @@ public class BoundSql { public Object[] getParamArray() { return this.params.toArray(new Object[0]); } + + /** + * 设置参数列表 + * + * @param params 参数列表 + * @return this + */ + public BoundSql setParams(final List params) { + this.params = params; + return this; + } + + /** + * 增加参数 + * + * @param paramValue 参数值 + * @return this + */ + public BoundSql addParam(final Object paramValue){ + if(null == this.params){ + this.params = new ArrayList<>(); + } + this.params.add(paramValue); + return this; + } } diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/NamedSql.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/NamedSql.java index db442abdf..9ba5354a8 100644 --- a/hutool-db/src/main/java/org/dromara/hutool/db/sql/NamedSql.java +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/NamedSql.java @@ -17,7 +17,6 @@ import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.text.StrUtil; import java.util.Collection; -import java.util.LinkedList; import java.util.Map; /** @@ -46,7 +45,6 @@ public class NamedSql extends BoundSql { * @param paramMap 名和参数的对应Map */ public NamedSql(final String namedSql, final Map paramMap) { - super(null, new LinkedList<>()); this.namedSql = namedSql; this.paramMap = paramMap; parse(namedSql, paramMap); @@ -78,7 +76,7 @@ public class NamedSql extends BoundSql { */ private void parse(final String namedSql, final Map paramMap) { if (MapUtil.isEmpty(paramMap)) { - this.sql = namedSql; + setSql(namedSql); return; } @@ -116,7 +114,7 @@ public class NamedSql extends BoundSql { replaceVar(nameStartChar, name, sqlBuilder, paramMap); } - this.sql = sqlBuilder.toString(); + setSql(sqlBuilder.toString()); } /** @@ -155,11 +153,11 @@ public class NamedSql extends BoundSql { sqlBuilder.append(','); } sqlBuilder.append('?'); - this.params.add(ArrayUtil.get(paramValue, i)); + addParam(ArrayUtil.get(paramValue, i)); } } else { sqlBuilder.append('?'); - this.params.add(paramValue); + addParam(paramValue); } } else { // 无变量对应值,原样输出 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 index 1eb0b930a..c60aa6d8d 100644 --- 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 @@ -13,17 +13,19 @@ package org.dromara.hutool.db.sql; import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.collection.ListUtil; 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 org.dromara.hutool.db.sql.filter.SqlFilter; import java.sql.*; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -44,20 +46,19 @@ public class StatementBuilder implements Builder { return new StatementBuilder(); } - private SqlLog sqlLog; + private final BoundSql boundSql = new BoundSql(); private Connection connection; - private String sql; - private Object[] params; private boolean returnGeneratedKey = true; + private SqlFilter sqlFilter; /** * 设置SQL日志 * - * @param sqlLog {@link SqlLog} + * @param sqlFilter {@link SqlFilter} * @return this */ - public StatementBuilder setSqlLog(final SqlLog sqlLog) { - this.sqlLog = sqlLog; + public StatementBuilder setSqlFilter(final SqlFilter sqlFilter) { + this.sqlFilter = sqlFilter; return this; } @@ -79,7 +80,7 @@ public class StatementBuilder implements Builder { * @return this */ public StatementBuilder setSql(final String sql) { - this.sql = StrUtil.trim(sql); + this.boundSql.setSql(sql); return this; } @@ -90,7 +91,7 @@ public class StatementBuilder implements Builder { * @return this */ public StatementBuilder setParams(final Object... params) { - this.params = params; + this.boundSql.setParams(ListUtil.of(params)); return this; } @@ -105,6 +106,11 @@ public class StatementBuilder implements Builder { return this; } + /** + * 构建{@link StatementWrapper} + * + * @return {@link StatementWrapper},{@code null}表示不执行 + */ @Override public StatementWrapper build() { try { @@ -117,48 +123,29 @@ public class StatementBuilder implements Builder { /** * 创建批量操作的{@link StatementWrapper} * - * @param paramsBatch "?"对应参数批次列表 - * @return {@link StatementWrapper} + * @return {@link StatementWrapper},{@code null}表示不执行 * @throws DbRuntimeException SQL异常 */ - public StatementWrapper buildForBatch(final Iterable paramsBatch) throws DbRuntimeException { + public StatementWrapper buildForBatch() throws DbRuntimeException { + final String sql = this.boundSql.getSql(); Assert.notBlank(sql, "Sql String must be not blank!"); + final List paramsBatch = this.boundSql.getParams(); - sqlLog.log(sql, paramsBatch); + sqlFilter.filter(this.connection, this.boundSql, this.returnGeneratedKey); 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); + for (final Object params : paramsBatch) { + if (null == params) { + continue; + } + if (ArrayUtil.isArray(params)) { + ps.fillParams(new ArrayIter<>(params), nullTypeMap); + } else if (params instanceof Entity) { + ps.fillParams(((Entity) params).values(), nullTypeMap); + } ps.addBatch(); } } catch (final SQLException e) { @@ -170,12 +157,15 @@ public class StatementBuilder implements Builder { /** * 创建存储过程或函数调用的{@link StatementWrapper} * - * @return StatementWrapper + * @return StatementWrapper,{@code null}表示不执行 * @since 6.0.0 */ public CallableStatement buildForCall() { + final String sql = this.boundSql.getSql(); + final Object[] params = this.boundSql.getParamArray(); Assert.notBlank(sql, "Sql String must be not blank!"); - sqlLog.log(sql, ArrayUtil.isEmpty(params) ? null : params); + + sqlFilter.filter(this.connection, this.boundSql, this.returnGeneratedKey); try { return (CallableStatement) StatementWrapper @@ -190,20 +180,23 @@ public class StatementBuilder implements Builder { /** * 构建{@link StatementWrapper} * - * @return {@link StatementWrapper} + * @return {@link StatementWrapper},{@code null}表示不执行 * @throws SQLException SQL异常 */ private StatementWrapper _build() throws SQLException { + String sql = this.boundSql.getSql(); + Object[] params = this.boundSql.getParamArray(); 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])); + final NamedSql namedSql = new NamedSql(sql, Convert.toMap(String.class, Object.class, params[0])); sql = namedSql.getSql(); params = namedSql.getParamArray(); } - sqlLog.log(sql, ArrayUtil.isEmpty(params) ? null : params); + sqlFilter.filter(this.connection, this.boundSql, this.returnGeneratedKey); + final PreparedStatement ps; if (returnGeneratedKey && StrUtil.startWithIgnoreCase(sql, "insert")) { // 插入默认返回主键 diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/SqlFilter.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/SqlFilter.java new file mode 100644 index 000000000..5a06a5445 --- /dev/null +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/SqlFilter.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024. 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: + * https://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.filter; + +import org.dromara.hutool.db.sql.BoundSql; + +import java.sql.Connection; +import java.util.List; + +/** + * SQL拦截器 + */ +public interface SqlFilter { + + /** + * 过滤 + * + * @param conn {@link Connection} + * @param boundSql {@link BoundSql},包含SQL语句和参数, + * 可通过{@link BoundSql#setSql(String)}和{@link BoundSql#setParams(List)} 自定义SQL和参数 + * @param returnGeneratedKey 是否自动生成主键 + */ + void filter(Connection conn, BoundSql boundSql, boolean returnGeneratedKey); +} diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/SqlLogFilter.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/SqlLogFilter.java new file mode 100644 index 000000000..53ec08480 --- /dev/null +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/SqlLogFilter.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024. 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: + * https://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.filter; + +import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.db.sql.BoundSql; +import org.dromara.hutool.db.sql.SqlLog; + +import java.sql.Connection; + +/** + * SQL打印拦截器 + * + * @author Looly + */ +public class SqlLogFilter implements SqlFilter { + + /** + * 单例 + */ + public static final SqlLogFilter INSTANCE = new SqlLogFilter(); + + private final SqlLog sqlLog; + + /** + * 构造,使用默认SqlLog + */ + public SqlLogFilter() { + this(SqlLog.INSTANCE); + } + + /** + * 构造 + * + * @param sqlLog {@link SqlLog} + */ + public SqlLogFilter(final SqlLog sqlLog) { + this.sqlLog = sqlLog; + } + + @Override + public void filter(final Connection conn, final BoundSql boundSql, final boolean returnGeneratedKey) { + sqlLog.log(boundSql.getSql(), boundSql.getParams()); + } +} diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/package-info.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/package-info.java new file mode 100644 index 000000000..e1ee60e95 --- /dev/null +++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/filter/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024. 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: + * https://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. + */ + +/** + * 提供SQL过滤器封装 + * + * @author Looly + */ +package org.dromara.hutool.db.sql.filter;