fix db bug

This commit is contained in:
Looly 2021-04-13 15:04:19 +08:00
parent 56e32846cd
commit 97e15f8152
4 changed files with 101 additions and 68 deletions

View File

@ -3,12 +3,13 @@
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.6.3 (2021-04-11) # 5.6.4 (2021-04-13)
### 新特性 ### 新特性
* 【core 】 DatePattern补充DateTimeFormatterpr#308@Gitee * 【core 】 DatePattern补充DateTimeFormatterpr#308@Gitee
### Bug修复 ### Bug修复
* 【db 】 修复SQL分页时未使用别名导致的错误同时count时取消order by子句issue#I3IJ8X@Gitee
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@ -1,7 +1,9 @@
package cn.hutool.db; package cn.hutool.db;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.dialect.Dialect; import cn.hutool.db.dialect.Dialect;
import cn.hutool.db.dialect.DialectFactory; import cn.hutool.db.dialect.DialectFactory;
import cn.hutool.db.handler.EntityListHandler; 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 conn 数据库连接对象
* @param selectSql 查询语句 * @param selectSql 查询语句
@ -287,7 +289,16 @@ public class SqlConnRunner extends DialectRunner {
* @throws SQLException SQL异常 * @throws SQLException SQL异常
*/ */
public long count(Connection conn, CharSequence selectSql) throws SQLException { 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(); 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); return this.page(conn, Query.of(where).setFields(fields).setPage(page), handler);
} }
//---------------------------------------------------------------------------- CRUD end //---------------------------------------------------------------------------- CRUD end
} }

View File

@ -19,17 +19,17 @@ import java.util.Map.Entry;
* SQL构建器<br> * SQL构建器<br>
* 首先拼接SQL语句值使用 ? 占位<br> * 首先拼接SQL语句值使用 ? 占位<br>
* 调用getParamValues()方法获得占位符对应的值 * 调用getParamValues()方法获得占位符对应的值
*
* @author Looly
* *
* @author Looly
*/ */
public class SqlBuilder implements Builder<String>{ public class SqlBuilder implements Builder<String> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
// --------------------------------------------------------------- Static methods start // --------------------------------------------------------------- Static methods start
/** /**
* 创建SQL构建器 * 创建SQL构建器
* *
* @return SQL构建器 * @return SQL构建器
*/ */
public static SqlBuilder create() { public static SqlBuilder create() {
@ -38,7 +38,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 创建SQL构建器 * 创建SQL构建器
* *
* @param wrapper 包装器 * @param wrapper 包装器
* @return SQL构建器 * @return SQL构建器
*/ */
@ -48,41 +48,56 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 从已有的SQL中构建一个SqlBuilder * 从已有的SQL中构建一个SqlBuilder
*
* @param sql SQL语句 * @param sql SQL语句
* @return SqlBuilder * @return SqlBuilder
* @since 5.5.3 * @since 5.5.3
*/ */
public static SqlBuilder of(CharSequence sql){ public static SqlBuilder of(CharSequence sql) {
return create().append(sql); return create().append(sql);
} }
// --------------------------------------------------------------- Static methods end // --------------------------------------------------------------- Static methods end
// --------------------------------------------------------------- Enums start // --------------------------------------------------------------- Enums start
/** /**
* SQL中多表关联用的关键字 * SQL中多表关联用的关键字
*
* @author Looly
* *
* @author Looly
*/ */
public enum Join { public enum Join {
/** 如果表中有至少一个匹配,则返回行 */ /**
* 如果表中有至少一个匹配则返回行
*/
INNER, INNER,
/** 即使右表中没有匹配,也从左表返回所有的行 */ /**
* 即使右表中没有匹配也从左表返回所有的行
*/
LEFT, LEFT,
/** 即使左表中没有匹配,也从右表返回所有的行 */ /**
* 即使左表中没有匹配也从右表返回所有的行
*/
RIGHT, RIGHT,
/** 只要其中一个表中存在匹配,就返回行 */ /**
* 只要其中一个表中存在匹配就返回行
*/
FULL FULL
} }
// --------------------------------------------------------------- Enums end // --------------------------------------------------------------- Enums end
private final StringBuilder sql = new StringBuilder(); private final StringBuilder sql = new StringBuilder();
/** 字段列表(仅用于插入和更新) */ /**
* 字段列表仅用于插入和更新
*/
private final List<String> fields = new ArrayList<>(); private final List<String> fields = new ArrayList<>();
/** 占位符对应的值列表 */ /**
* 占位符对应的值列表
*/
private final List<Object> paramValues = new ArrayList<>(); private final List<Object> paramValues = new ArrayList<>();
/** 包装器 */ /**
* 包装器
*/
private Wrapper wrapper; private Wrapper wrapper;
// --------------------------------------------------------------- Constructor start // --------------------------------------------------------------- Constructor start
@ -98,7 +113,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 插入使用默认的ANSI方言 * 插入使用默认的ANSI方言
* *
* @param entity 实体 * @param entity 实体
* @return 自己 * @return 自己
*/ */
@ -110,7 +125,7 @@ public class SqlBuilder implements Builder<String>{
* 插入<br> * 插入<br>
* 插入会忽略空的字段名及其对应值但是对于有字段名对应值为{@code null}的情况不忽略 * 插入会忽略空的字段名及其对应值但是对于有字段名对应值为{@code null}的情况不忽略
* *
* @param entity 实体 * @param entity 实体
* @param dialectName 方言名用于对特殊数据库特殊处理 * @param dialectName 方言名用于对特殊数据库特殊处理
* @return 自己 * @return 自己
*/ */
@ -121,8 +136,8 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 插入<br> * 插入<br>
* 插入会忽略空的字段名及其对应值但是对于有字段名对应值为{@code null}的情况不忽略 * 插入会忽略空的字段名及其对应值但是对于有字段名对应值为{@code null}的情况不忽略
* *
* @param entity 实体 * @param entity 实体
* @param dialectName 方言名用于对特殊数据库特殊处理 * @param dialectName 方言名用于对特殊数据库特殊处理
* @return 自己 * @return 自己
* @since 5.5.3 * @since 5.5.3
@ -132,8 +147,7 @@ public class SqlBuilder implements Builder<String>{
validateEntity(entity); validateEntity(entity);
if (null != wrapper) { if (null != wrapper) {
// 包装表名 // 包装表名 entity = wrapper.wrap(entity);
// entity = wrapper.wrap(entity);
entity.setTableName(wrapper.wrap(entity.getTableName())); entity.setTableName(wrapper.wrap(entity.getTableName()));
} }
@ -169,14 +183,14 @@ public class SqlBuilder implements Builder<String>{
} }
sql.append("INSERT INTO ")// sql.append("INSERT INTO ")//
.append(entity.getTableName()).append(" (").append(fieldsPart).append(") VALUES (")// .append(entity.getTableName()).append(" (").append(fieldsPart).append(") VALUES (")//
.append(placeHolder.toString()).append(")"); .append(placeHolder).append(")");
return this; return this;
} }
/** /**
* 删除 * 删除
* *
* @param tableName 表名 * @param tableName 表名
* @return 自己 * @return 自己
*/ */
@ -197,7 +211,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 更新 * 更新
* *
* @param entity 要更新的实体 * @param entity 要更新的实体
* @return 自己 * @return 自己
*/ */
@ -230,9 +244,9 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 查询 * 查询
* *
* @param isDistinct 是否添加DISTINCT关键字查询唯一结果 * @param isDistinct 是否添加DISTINCT关键字查询唯一结果
* @param fields 查询的字段 * @param fields 查询的字段
* @return 自己 * @return 自己
*/ */
public SqlBuilder select(boolean isDistinct, String... fields) { public SqlBuilder select(boolean isDistinct, String... fields) {
@ -241,9 +255,9 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 查询 * 查询
* *
* @param isDistinct 是否添加DISTINCT关键字查询唯一结果 * @param isDistinct 是否添加DISTINCT关键字查询唯一结果
* @param fields 查询的字段 * @param fields 查询的字段
* @return 自己 * @return 自己
*/ */
public SqlBuilder select(boolean isDistinct, Collection<String> fields) { public SqlBuilder select(boolean isDistinct, Collection<String> fields) {
@ -267,7 +281,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 查询非Distinct * 查询非Distinct
* *
* @param fields 查询的字段 * @param fields 查询的字段
* @return 自己 * @return 自己
*/ */
@ -277,7 +291,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 查询非Distinct * 查询非Distinct
* *
* @param fields 查询的字段 * @param fields 查询的字段
* @return 自己 * @return 自己
*/ */
@ -287,7 +301,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 添加 from语句 * 添加 from语句
* *
* @param tableNames 表名列表多个表名用于多表查询 * @param tableNames 表名列表多个表名用于多表查询
* @return 自己 * @return 自己
*/ */
@ -305,10 +319,10 @@ public class SqlBuilder implements Builder<String>{
return this; return this;
} }
/** /**
* 添加Where语句所有逻辑之间关系使用{@link Condition#setLinkOperator(LogicalOperator)} 定义 * 添加Where语句所有逻辑之间关系使用{@link Condition#setLinkOperator(LogicalOperator)} 定义
* *
* @param conditions 条件当条件为空时只添加WHERE关键字 * @param conditions 条件当条件为空时只添加WHERE关键字
* @return 自己 * @return 自己
* @since 4.4.4 * @since 4.4.4
@ -324,9 +338,9 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 添加Where语句<br> * 添加Where语句<br>
* 只支持单一的逻辑运算符例如多个条件之间 * 只支持单一的逻辑运算符例如多个条件之间
* *
* @param logicalOperator 逻辑运算符 * @param logicalOperator 逻辑运算符
* @param conditions 条件当条件为空时只添加WHERE关键字 * @param conditions 条件当条件为空时只添加WHERE关键字
* @return 自己 * @return 自己
* @deprecated logicalOperator放在Condition中了因此请使用 {@link #where(Condition...)} * @deprecated logicalOperator放在Condition中了因此请使用 {@link #where(Condition...)}
*/ */
@ -337,7 +351,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 添加Where语句<br> * 添加Where语句<br>
* *
* @param where WHERE语句之后跟的条件语句字符串 * @param where WHERE语句之后跟的条件语句字符串
* @return 自己 * @return 自己
*/ */
@ -350,9 +364,9 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 多值选择 * 多值选择
* *
* @param <T> 值类型 * @param <T> 值类型
* @param field 字段名 * @param field 字段名
* @param values 值列表 * @param values 值列表
* @return 自身 * @return 自身
*/ */
@ -364,7 +378,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 分组 * 分组
* *
* @param fields 字段 * @param fields 字段
* @return 自己 * @return 自己
*/ */
@ -383,9 +397,9 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 添加Having语句 * 添加Having语句
* *
* @param logicalOperator 逻辑运算符 * @param logicalOperator 逻辑运算符
* @param conditions 条件 * @param conditions 条件
* @return 自己 * @return 自己
* @deprecated logicalOperator放在Condition中了因此请使用 {@link #having(Condition...)} * @deprecated logicalOperator放在Condition中了因此请使用 {@link #having(Condition...)}
*/ */
@ -411,7 +425,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 添加Having语句 * 添加Having语句
* *
* @param having 条件语句 * @param having 条件语句
* @return 自己 * @return 自己
*/ */
@ -424,7 +438,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 排序 * 排序
* *
* @param orders 排序对象 * @param orders 排序对象
* @return 自己 * @return 自己
*/ */
@ -463,9 +477,9 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 多表关联 * 多表关联
* *
* @param tableName 被关联的表名 * @param tableName 被关联的表名
* @param join 内联方式 * @param join 内联方式
* @return 自己 * @return 自己
*/ */
public SqlBuilder join(String tableName, Join join) { public SqlBuilder join(String tableName, Join join) {
@ -487,9 +501,9 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 配合JOIN的 ON语句多表关联的条件语句<br> * 配合JOIN的 ON语句多表关联的条件语句<br>
* 只支持单一的逻辑运算符例如多个条件之间 * 只支持单一的逻辑运算符例如多个条件之间
* *
* @param logicalOperator 逻辑运算符 * @param logicalOperator 逻辑运算符
* @param conditions 条件 * @param conditions 条件
* @return 自己 * @return 自己
* @deprecated logicalOperator放在Condition中了因此请使用 {@link #on(Condition...)} * @deprecated logicalOperator放在Condition中了因此请使用 {@link #on(Condition...)}
*/ */
@ -516,7 +530,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 配合JOIN的 ON语句多表关联的条件语句<br> * 配合JOIN的 ON语句多表关联的条件语句<br>
* 只支持单一的逻辑运算符例如多个条件之间 * 只支持单一的逻辑运算符例如多个条件之间
* *
* @param on 条件 * @param on 条件
* @return 自己 * @return 自己
*/ */
@ -529,7 +543,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 在SQL的开头补充SQL片段 * 在SQL的开头补充SQL片段
* *
* @param sqlFragment SQL片段 * @param sqlFragment SQL片段
* @return this * @return this
* @since 4.1.3 * @since 4.1.3
@ -548,9 +562,9 @@ public class SqlBuilder implements Builder<String>{
* SqlBuilder builder = SqlBuilder.of("select *"); * SqlBuilder builder = SqlBuilder.of("select *");
* builder.append(" from ").append("user"); * builder.append(" from ").append("user");
* </pre> * </pre>
* * <p>
* 如果需要追加带占位符的片段需调用{@link #addParams(Object...)} 方法加入对应参数值 * 如果需要追加带占位符的片段需调用{@link #addParams(Object...)} 方法加入对应参数值
* *
* @param sqlFragment SQL其它部分片段 * @param sqlFragment SQL其它部分片段
* @return this * @return this
*/ */
@ -574,8 +588,8 @@ public class SqlBuilder implements Builder<String>{
* @return this * @return this
* @since 5.5.3 * @since 5.5.3
*/ */
public SqlBuilder addParams(Object... params){ public SqlBuilder addParams(Object... params) {
if(ArrayUtil.isNotEmpty(params)){ if (ArrayUtil.isNotEmpty(params)) {
Collections.addAll(this.paramValues, params); Collections.addAll(this.paramValues, params);
} }
return this; return this;
@ -583,7 +597,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 构建查询SQL * 构建查询SQL
* *
* @param query {@link Query} * @param query {@link Query}
* @return this * @return this
*/ */
@ -612,7 +626,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 获得占位符对应的值列表<br> * 获得占位符对应的值列表<br>
* *
* @return 占位符对应的值列表 * @return 占位符对应的值列表
*/ */
public List<Object> getParamValues() { public List<Object> getParamValues() {
@ -621,7 +635,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 获得占位符对应的值列表<br> * 获得占位符对应的值列表<br>
* *
* @return 占位符对应的值列表 * @return 占位符对应的值列表
*/ */
public Object[] getParamValueArray() { public Object[] getParamValueArray() {
@ -630,7 +644,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 构建默认打印SQL日志 * 构建默认打印SQL日志
* *
* @return 构建好的SQL语句 * @return 构建好的SQL语句
*/ */
@Override @Override
@ -644,10 +658,11 @@ public class SqlBuilder implements Builder<String>{
} }
// --------------------------------------------------------------- private method start // --------------------------------------------------------------- private method start
/** /**
* 构建组合条件<br> * 构建组合条件<br>
* 例如name = ? AND type IN (?, ?) AND other LIKE ? * 例如name = ? AND type IN (?, ?) AND other LIKE ?
* *
* @param conditions 条件对象 * @param conditions 条件对象
* @return 构建后的SQL语句条件部分 * @return 构建后的SQL语句条件部分
*/ */
@ -666,7 +681,7 @@ public class SqlBuilder implements Builder<String>{
/** /**
* 验证实体类对象的有效性 * 验证实体类对象的有效性
* *
* @param entity 实体类对象 * @param entity 实体类对象
* @throws DbRuntimeException SQL异常包装获取元数据信息失败 * @throws DbRuntimeException SQL异常包装获取元数据信息失败
*/ */

View File

@ -21,7 +21,7 @@ public class DbTest {
List<Entity> find = Db.use().query("select * from user where age = ?", 18); List<Entity> find = Db.use().query("select * from user where age = ?", 18);
Assert.assertEquals("王五", find.get(0).get("name")); Assert.assertEquals("王五", find.get(0).get("name"));
} }
@Test @Test
public void findTest() throws SQLException { public void findTest() throws SQLException {
List<Entity> find = Db.use().find(Entity.create("user").set("age", 18)); List<Entity> find = Db.use().find(Entity.create("user").set("age", 18));
@ -39,7 +39,7 @@ public class DbTest {
@Test @Test
public void pageTest2() throws SQLException { public void pageTest2() throws SQLException {
String sql = "select * from user"; String sql = "select * from user order by name";
// 测试数据库中一共4条数据第0页有3条第1页有1条 // 测试数据库中一共4条数据第0页有3条第1页有1条
List<Entity> page0 = Db.use().page( List<Entity> page0 = Db.use().page(
sql, Page.of(0, 3)); sql, Page.of(0, 3));
@ -56,6 +56,12 @@ public class DbTest {
Assert.assertEquals(4, count); 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 @Test
public void findLikeTest() throws SQLException { public void findLikeTest() throws SQLException {
// 方式1 // 方式1
@ -74,7 +80,7 @@ public class DbTest {
@Test @Test
public void findByTest() throws SQLException { public void findByTest() throws SQLException {
List<Entity> find = Db.use().findBy("user", List<Entity> find = Db.use().findBy("user",
Condition.parse("age", "> 18"), Condition.parse("age", "> 18"),
Condition.parse("age", "< 100") Condition.parse("age", "< 100")
); );
for (Entity entity : find) { for (Entity entity : find) {
@ -82,7 +88,7 @@ public class DbTest {
} }
Assert.assertEquals("unitTestUser", find.get(0).get("name")); Assert.assertEquals("unitTestUser", find.get(0).get("name"));
} }
@Test @Test
@Ignore @Ignore
public void txTest() throws SQLException { public void txTest() throws SQLException {