From 97e15f81524da83fefb4a4fb716949cf318efa1d Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 13 Apr 2021 15:04:19 +0800 Subject: [PATCH] fix db bug --- CHANGELOG.md | 3 +- .../main/java/cn/hutool/db/SqlConnRunner.java | 17 ++- .../java/cn/hutool/db/sql/SqlBuilder.java | 135 ++++++++++-------- .../src/test/java/cn/hutool/db/DbTest.java | 14 +- 4 files changed, 101 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39a67c26e..caf3301b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,13 @@ ------------------------------------------------------------------------------------------------------------- -# 5.6.3 (2021-04-11) +# 5.6.4 (2021-04-13) ### 新特性 * 【core 】 DatePattern补充DateTimeFormatter(pr#308@Gitee) ### Bug修复 +* 【db 】 修复SQL分页时未使用别名导致的错误,同时count时取消order by子句(issue#I3IJ8X@Gitee) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-db/src/main/java/cn/hutool/db/SqlConnRunner.java b/hutool-db/src/main/java/cn/hutool/db/SqlConnRunner.java index 795016047..961bfaafa 100644 --- a/hutool-db/src/main/java/cn/hutool/db/SqlConnRunner.java +++ b/hutool-db/src/main/java/cn/hutool/db/SqlConnRunner.java @@ -1,7 +1,9 @@ package cn.hutool.db; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.db.dialect.Dialect; import cn.hutool.db.dialect.DialectFactory; import cn.hutool.db.handler.EntityListHandler; @@ -279,7 +281,7 @@ public class SqlConnRunner extends DialectRunner { } /** - * 获取查询结果总数,生成类似于 SELECT count(1) from (sql) + * 获取查询结果总数,生成类似于 SELECT count(1) from (sql) as _count * * @param conn 数据库连接对象 * @param selectSql 查询语句 @@ -287,7 +289,16 @@ public class SqlConnRunner extends DialectRunner { * @throws SQLException SQL异常 */ public long count(Connection conn, CharSequence selectSql) throws SQLException { - SqlBuilder sqlBuilder = SqlBuilder.of(selectSql).insertPreFragment("SELECT count(1) from(").append(")"); + Assert.notBlank(selectSql, "Select SQL must be not blank!"); + final int orderByIndex = StrUtil.indexOfIgnoreCase(selectSql, " order by"); + if(orderByIndex > 0){ + selectSql = StrUtil.subPre(selectSql, orderByIndex); + } + + SqlBuilder sqlBuilder = SqlBuilder.of(selectSql) + .insertPreFragment("SELECT count(1) from(") + // issue#I3IJ8X@Gitee,在子查询时需设置单独别名,此处为了防止和用户的表名冲突,使用自定义的较长别名 + .append(") as _hutool_alias_count_"); return page(conn, sqlBuilder, null, new NumberHandler()).intValue(); } @@ -392,4 +403,4 @@ public class SqlConnRunner extends DialectRunner { return this.page(conn, Query.of(where).setFields(fields).setPage(page), handler); } //---------------------------------------------------------------------------- CRUD end -} \ No newline at end of file +} diff --git a/hutool-db/src/main/java/cn/hutool/db/sql/SqlBuilder.java b/hutool-db/src/main/java/cn/hutool/db/sql/SqlBuilder.java index 42c0d6aa5..037a51648 100644 --- a/hutool-db/src/main/java/cn/hutool/db/sql/SqlBuilder.java +++ b/hutool-db/src/main/java/cn/hutool/db/sql/SqlBuilder.java @@ -19,17 +19,17 @@ import java.util.Map.Entry; * SQL构建器
* 首先拼接SQL语句,值使用 ? 占位
* 调用getParamValues()方法获得占位符对应的值 - * - * @author Looly * + * @author Looly */ -public class SqlBuilder implements Builder{ +public class SqlBuilder implements Builder { private static final long serialVersionUID = 1L; // --------------------------------------------------------------- Static methods start + /** * 创建SQL构建器 - * + * * @return SQL构建器 */ public static SqlBuilder create() { @@ -38,7 +38,7 @@ public class SqlBuilder implements Builder{ /** * 创建SQL构建器 - * + * * @param wrapper 包装器 * @return SQL构建器 */ @@ -48,41 +48,56 @@ public class SqlBuilder implements Builder{ /** * 从已有的SQL中构建一个SqlBuilder + * * @param sql SQL语句 * @return SqlBuilder * @since 5.5.3 */ - public static SqlBuilder of(CharSequence sql){ + public static SqlBuilder of(CharSequence sql) { return create().append(sql); } // --------------------------------------------------------------- Static methods end // --------------------------------------------------------------- Enums start + /** * SQL中多表关联用的关键字 - * - * @author Looly * + * @author Looly */ public enum Join { - /** 如果表中有至少一个匹配,则返回行 */ + /** + * 如果表中有至少一个匹配,则返回行 + */ INNER, - /** 即使右表中没有匹配,也从左表返回所有的行 */ + /** + * 即使右表中没有匹配,也从左表返回所有的行 + */ LEFT, - /** 即使左表中没有匹配,也从右表返回所有的行 */ + /** + * 即使左表中没有匹配,也从右表返回所有的行 + */ RIGHT, - /** 只要其中一个表中存在匹配,就返回行 */ + /** + * 只要其中一个表中存在匹配,就返回行 + */ FULL } // --------------------------------------------------------------- Enums end private final StringBuilder sql = new StringBuilder(); - /** 字段列表(仅用于插入和更新) */ + /** + * 字段列表(仅用于插入和更新) + */ private final List fields = new ArrayList<>(); - /** 占位符对应的值列表 */ + /** + * 占位符对应的值列表 + */ private final List paramValues = new ArrayList<>(); - /** 包装器 */ + /** + * 包装器 + */ private Wrapper wrapper; // --------------------------------------------------------------- Constructor start @@ -98,7 +113,7 @@ public class SqlBuilder implements Builder{ /** * 插入,使用默认的ANSI方言 - * + * * @param entity 实体 * @return 自己 */ @@ -110,7 +125,7 @@ public class SqlBuilder implements Builder{ * 插入
* 插入会忽略空的字段名及其对应值,但是对于有字段名对应值为{@code null}的情况不忽略 * - * @param entity 实体 + * @param entity 实体 * @param dialectName 方言名,用于对特殊数据库特殊处理 * @return 自己 */ @@ -121,8 +136,8 @@ public class SqlBuilder implements Builder{ /** * 插入
* 插入会忽略空的字段名及其对应值,但是对于有字段名对应值为{@code null}的情况不忽略 - * - * @param entity 实体 + * + * @param entity 实体 * @param dialectName 方言名,用于对特殊数据库特殊处理 * @return 自己 * @since 5.5.3 @@ -132,8 +147,7 @@ public class SqlBuilder implements Builder{ validateEntity(entity); if (null != wrapper) { - // 包装表名 - // entity = wrapper.wrap(entity); + // 包装表名 entity = wrapper.wrap(entity); entity.setTableName(wrapper.wrap(entity.getTableName())); } @@ -169,14 +183,14 @@ public class SqlBuilder implements Builder{ } sql.append("INSERT INTO ")// .append(entity.getTableName()).append(" (").append(fieldsPart).append(") VALUES (")// - .append(placeHolder.toString()).append(")"); + .append(placeHolder).append(")"); return this; } /** * 删除 - * + * * @param tableName 表名 * @return 自己 */ @@ -197,7 +211,7 @@ public class SqlBuilder implements Builder{ /** * 更新 - * + * * @param entity 要更新的实体 * @return 自己 */ @@ -230,9 +244,9 @@ public class SqlBuilder implements Builder{ /** * 查询 - * + * * @param isDistinct 是否添加DISTINCT关键字(查询唯一结果) - * @param fields 查询的字段 + * @param fields 查询的字段 * @return 自己 */ public SqlBuilder select(boolean isDistinct, String... fields) { @@ -241,9 +255,9 @@ public class SqlBuilder implements Builder{ /** * 查询 - * + * * @param isDistinct 是否添加DISTINCT关键字(查询唯一结果) - * @param fields 查询的字段 + * @param fields 查询的字段 * @return 自己 */ public SqlBuilder select(boolean isDistinct, Collection fields) { @@ -267,7 +281,7 @@ public class SqlBuilder implements Builder{ /** * 查询(非Distinct) - * + * * @param fields 查询的字段 * @return 自己 */ @@ -277,7 +291,7 @@ public class SqlBuilder implements Builder{ /** * 查询(非Distinct) - * + * * @param fields 查询的字段 * @return 自己 */ @@ -287,7 +301,7 @@ public class SqlBuilder implements Builder{ /** * 添加 from语句 - * + * * @param tableNames 表名列表(多个表名用于多表查询) * @return 自己 */ @@ -305,10 +319,10 @@ public class SqlBuilder implements Builder{ return this; } - + /** * 添加Where语句,所有逻辑之间关系使用{@link Condition#setLinkOperator(LogicalOperator)} 定义 - * + * * @param conditions 条件,当条件为空时,只添加WHERE关键字 * @return 自己 * @since 4.4.4 @@ -324,9 +338,9 @@ public class SqlBuilder implements Builder{ /** * 添加Where语句
* 只支持单一的逻辑运算符(例如多个条件之间) - * + * * @param logicalOperator 逻辑运算符 - * @param conditions 条件,当条件为空时,只添加WHERE关键字 + * @param conditions 条件,当条件为空时,只添加WHERE关键字 * @return 自己 * @deprecated logicalOperator放在Condition中了,因此请使用 {@link #where(Condition...)} */ @@ -337,7 +351,7 @@ public class SqlBuilder implements Builder{ /** * 添加Where语句
- * + * * @param where WHERE语句之后跟的条件语句字符串 * @return 自己 */ @@ -350,9 +364,9 @@ public class SqlBuilder implements Builder{ /** * 多值选择 - * - * @param 值类型 - * @param field 字段名 + * + * @param 值类型 + * @param field 字段名 * @param values 值列表 * @return 自身 */ @@ -364,7 +378,7 @@ public class SqlBuilder implements Builder{ /** * 分组 - * + * * @param fields 字段 * @return 自己 */ @@ -383,9 +397,9 @@ public class SqlBuilder implements Builder{ /** * 添加Having语句 - * + * * @param logicalOperator 逻辑运算符 - * @param conditions 条件 + * @param conditions 条件 * @return 自己 * @deprecated logicalOperator放在Condition中了,因此请使用 {@link #having(Condition...)} */ @@ -411,7 +425,7 @@ public class SqlBuilder implements Builder{ /** * 添加Having语句 - * + * * @param having 条件语句 * @return 自己 */ @@ -424,7 +438,7 @@ public class SqlBuilder implements Builder{ /** * 排序 - * + * * @param orders 排序对象 * @return 自己 */ @@ -463,9 +477,9 @@ public class SqlBuilder implements Builder{ /** * 多表关联 - * + * * @param tableName 被关联的表名 - * @param join 内联方式 + * @param join 内联方式 * @return 自己 */ public SqlBuilder join(String tableName, Join join) { @@ -487,9 +501,9 @@ public class SqlBuilder implements Builder{ /** * 配合JOIN的 ON语句,多表关联的条件语句
* 只支持单一的逻辑运算符(例如多个条件之间) - * + * * @param logicalOperator 逻辑运算符 - * @param conditions 条件 + * @param conditions 条件 * @return 自己 * @deprecated logicalOperator放在Condition中了,因此请使用 {@link #on(Condition...)} */ @@ -516,7 +530,7 @@ public class SqlBuilder implements Builder{ /** * 配合JOIN的 ON语句,多表关联的条件语句
* 只支持单一的逻辑运算符(例如多个条件之间) - * + * * @param on 条件 * @return 自己 */ @@ -529,7 +543,7 @@ public class SqlBuilder implements Builder{ /** * 在SQL的开头补充SQL片段 - * + * * @param sqlFragment SQL片段 * @return this * @since 4.1.3 @@ -548,9 +562,9 @@ public class SqlBuilder implements Builder{ * SqlBuilder builder = SqlBuilder.of("select *"); * builder.append(" from ").append("user"); * - * + *

* 如果需要追加带占位符的片段,需调用{@link #addParams(Object...)} 方法加入对应参数值。 - * + * * @param sqlFragment SQL其它部分片段 * @return this */ @@ -574,8 +588,8 @@ public class SqlBuilder implements Builder{ * @return this * @since 5.5.3 */ - public SqlBuilder addParams(Object... params){ - if(ArrayUtil.isNotEmpty(params)){ + public SqlBuilder addParams(Object... params) { + if (ArrayUtil.isNotEmpty(params)) { Collections.addAll(this.paramValues, params); } return this; @@ -583,7 +597,7 @@ public class SqlBuilder implements Builder{ /** * 构建查询SQL - * + * * @param query {@link Query} * @return this */ @@ -612,7 +626,7 @@ public class SqlBuilder implements Builder{ /** * 获得占位符对应的值列表
- * + * * @return 占位符对应的值列表 */ public List getParamValues() { @@ -621,7 +635,7 @@ public class SqlBuilder implements Builder{ /** * 获得占位符对应的值列表
- * + * * @return 占位符对应的值列表 */ public Object[] getParamValueArray() { @@ -630,7 +644,7 @@ public class SqlBuilder implements Builder{ /** * 构建,默认打印SQL日志 - * + * * @return 构建好的SQL语句 */ @Override @@ -644,10 +658,11 @@ public class SqlBuilder implements Builder{ } // --------------------------------------------------------------- private method start + /** * 构建组合条件
* 例如:name = ? AND type IN (?, ?) AND other LIKE ? - * + * * @param conditions 条件对象 * @return 构建后的SQL语句条件部分 */ @@ -666,7 +681,7 @@ public class SqlBuilder implements Builder{ /** * 验证实体类对象的有效性 - * + * * @param entity 实体类对象 * @throws DbRuntimeException SQL异常包装,获取元数据信息失败 */ diff --git a/hutool-db/src/test/java/cn/hutool/db/DbTest.java b/hutool-db/src/test/java/cn/hutool/db/DbTest.java index b2e2cc82e..e599d3146 100644 --- a/hutool-db/src/test/java/cn/hutool/db/DbTest.java +++ b/hutool-db/src/test/java/cn/hutool/db/DbTest.java @@ -21,7 +21,7 @@ public class DbTest { List find = Db.use().query("select * from user where age = ?", 18); Assert.assertEquals("王五", find.get(0).get("name")); } - + @Test public void findTest() throws SQLException { List find = Db.use().find(Entity.create("user").set("age", 18)); @@ -39,7 +39,7 @@ public class DbTest { @Test public void pageTest2() throws SQLException { - String sql = "select * from user"; + String sql = "select * from user order by name"; // 测试数据库中一共4条数据,第0页有3条,第1页有1条 List page0 = Db.use().page( sql, Page.of(0, 3)); @@ -56,6 +56,12 @@ public class DbTest { Assert.assertEquals(4, count); } + @Test + public void countTest2() throws SQLException { + final long count = Db.use().count("select * from user order by name DESC"); + Assert.assertEquals(4, count); + } + @Test public void findLikeTest() throws SQLException { // 方式1 @@ -74,7 +80,7 @@ public class DbTest { @Test public void findByTest() throws SQLException { List find = Db.use().findBy("user", - Condition.parse("age", "> 18"), + Condition.parse("age", "> 18"), Condition.parse("age", "< 100") ); for (Entity entity : find) { @@ -82,7 +88,7 @@ public class DbTest { } Assert.assertEquals("unitTestUser", find.get(0).get("name")); } - + @Test @Ignore public void txTest() throws SQLException {