add ConditionBuilder

This commit is contained in:
Looly 2020-09-18 11:27:26 +08:00
parent 75a6da5b5f
commit 5a73a17f4d
8 changed files with 217 additions and 49 deletions

View File

@ -20,6 +20,7 @@
* 【core 】 IdcardUtil增加getIdcardInfo方法issue#1092@Github
* 【core 】 改进ObjectUtil.equal支持BigDecimal判断
* 【core 】 ArrayConverter增加可选是否忽略错误issue#I1VNYQ@Gitee
* 【db 】 增加ConditionBuilder
### Bug修复
* 【core 】 修复Dict.of错误issue#I1UUO5@Gitee

View File

@ -478,6 +478,9 @@ public class ListUtil {
* @since 5.2.6
*/
public static <T> List<T> unmodifiable(List<T> list) {
if(null == list){
return null;
}
return Collections.unmodifiableList(list);
}

View File

@ -10,6 +10,7 @@ import cn.hutool.db.handler.RsHandler;
import cn.hutool.db.handler.StringHandler;
import cn.hutool.db.sql.Condition;
import cn.hutool.db.sql.Condition.LikeType;
import cn.hutool.db.sql.LogicalOperator;
import cn.hutool.db.sql.Query;
import cn.hutool.db.sql.SqlExecutor;
import cn.hutool.db.sql.SqlUtil;
@ -628,7 +629,7 @@ public abstract class AbstractDb implements Serializable {
* 根据多个条件查询数据列表返回所有字段
*
* @param tableName 表名
* @param wheres 字段名
* @param wheres 条件多个条件的连接逻辑使用{@link Condition#setLinkOperator(LogicalOperator)} 定义
* @return 数据对象列表
* @throws SQLException SQL执行异常
* @since 4.0.0

View File

@ -11,7 +11,6 @@ import cn.hutool.db.StatementUtil;
import cn.hutool.db.dialect.Dialect;
import cn.hutool.db.dialect.DialectName;
import cn.hutool.db.sql.Condition;
import cn.hutool.db.sql.LogicalOperator;
import cn.hutool.db.sql.Query;
import cn.hutool.db.sql.SqlBuilder;
import cn.hutool.db.sql.Wrapper;
@ -67,7 +66,7 @@ public class AnsiSqlDialect implements Dialect {
// 对于无条件的删除语句直接抛出异常禁止防止误删除
throw new SQLException("No 'WHERE' condition, we can't prepared statement for delete everything.");
}
final SqlBuilder delete = SqlBuilder.create(wrapper).delete(query.getFirstTableName()).where(LogicalOperator.AND, where);
final SqlBuilder delete = SqlBuilder.create(wrapper).delete(query.getFirstTableName()).where(where);
return StatementUtil.prepareStatement(conn, delete);
}
@ -76,13 +75,13 @@ public class AnsiSqlDialect implements Dialect {
public PreparedStatement psForUpdate(Connection conn, Entity entity, Query query) throws SQLException {
Assert.notNull(query, "query must not be null !");
Condition[] where = query.getWhere();
final Condition[] where = query.getWhere();
if (ArrayUtil.isEmpty(where)) {
// 对于无条件的删除语句直接抛出异常禁止防止误删除
throw new SQLException("No 'WHERE' condition, we can't prepare statement for update everything.");
}
final SqlBuilder update = SqlBuilder.create(wrapper).update(entity).where(LogicalOperator.AND, where);
final SqlBuilder update = SqlBuilder.create(wrapper).update(entity).where(where);
return StatementUtil.prepareStatement(conn, update);
}

View File

@ -68,6 +68,11 @@ public class Condition extends CloneSupport<Condition> {
*/
private Object secondValue;
/**
* 与前一个Condition连接的逻辑运算符可以是and或or
*/
private LogicalOperator linkOperator = LogicalOperator.AND;
/**
* 解析为Condition
*
@ -282,6 +287,26 @@ public class Condition extends CloneSupport<Condition> {
this.secondValue = secondValue;
}
/**
* 获取与前一个Condition连接的逻辑运算符可以是and或or
*
* @return 与前一个Condition连接的逻辑运算符可以是and或or
* @since 5.4.3
*/
public LogicalOperator getLinkOperator() {
return linkOperator;
}
/**
* 设置与前一个Condition连接的逻辑运算符可以是and或or
*
* @param linkOperator 与前一个Condition连接的逻辑运算符可以是and或or
* @since 5.4.3
*/
public void setLinkOperator(LogicalOperator linkOperator) {
this.linkOperator = linkOperator;
}
// --------------------------------------------------------------- Getters and Setters end
@Override

View File

@ -0,0 +1,116 @@
package cn.hutool.db.sql;
import cn.hutool.core.builder.Builder;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.List;
/**
* 多条件构建封装<br>
* 可以将多个条件构建为SQL语句的一部分并将参数值转换为占位符并提取对应位置的参数值<br>
* 例如name = ? AND type IN (?, ?) AND other LIKE ?
*
* @author looly
* @since 5.4.3
*/
public class ConditionBuilder implements Builder<String> {
private static final long serialVersionUID = 1L;
/**
* 创建构建器
*
* @param conditions 条件列表
* @return {@link ConditionBuilder}
*/
public static ConditionBuilder of(Condition... conditions) {
return new ConditionBuilder(conditions);
}
/**
* 条件数组
*/
private final Condition[] conditions;
/**
* 占位符对应的值列表
*/
private List<Object> paramValues;
/**
* 构造
*
* @param conditions 条件列表
*/
public ConditionBuilder(Condition... conditions) {
this.conditions = conditions;
}
/**
* 返回构建后的参数列表<br>
* 此方法调用前必须调用{@link #build()}
*
* @return 参数列表
*/
public List<Object> getParamValues() {
return ListUtil.unmodifiable(this.paramValues);
}
/**
* 构建组合条件<br>
* 例如name = ? AND type IN (?, ?) AND other LIKE ?
*
* @return 构建后的SQL语句条件部分
*/
@Override
public String build() {
if(null == this.paramValues){
this.paramValues = new ArrayList<>();
} else {
this.paramValues.clear();
}
return build(this.paramValues);
}
/**
* 构建组合条件<br>
* 例如name = ? AND type IN (?, ?) AND other LIKE ?
*
* @param paramValues 用于写出参数的List,构建时会将参数写入此List
* @return 构建后的SQL语句条件部分
*/
public String build(List<Object> paramValues) {
if (ArrayUtil.isEmpty(conditions)) {
return StrUtil.EMPTY;
}
final StringBuilder conditionStrBuilder = new StringBuilder();
boolean isFirst = true;
for (Condition condition : conditions) {
// 添加逻辑运算符
if (isFirst) {
isFirst = false;
} else {
// " AND " 或者 " OR "
conditionStrBuilder.append(CharUtil.SPACE).append(condition.getLinkOperator()).append(CharUtil.SPACE);
}
// 构建条件部分"name = ?""name IN (?,?,?)""name BETWEEN AND ""name LIKE ?"
conditionStrBuilder.append(condition.toString(paramValues));
}
return conditionStrBuilder.toString();
}
/**
* 构建组合条件<br>
* 例如name = ? AND type IN (?, ?) AND other LIKE ?
*
* @return 构建后的SQL语句条件部分
*/
@Override
public String toString() {
return build();
}
}

View File

@ -67,11 +67,11 @@ public class SqlBuilder implements Builder<String>{
}
// --------------------------------------------------------------- Enums end
final private StringBuilder sql = new StringBuilder();
private final StringBuilder sql = new StringBuilder();
/** 字段列表(仅用于插入和更新) */
final private List<String> fields = new ArrayList<>();
private final List<String> fields = new ArrayList<>();
/** 占位符对应的值列表 */
final private List<Object> paramValues = new ArrayList<>();
private final List<Object> paramValues = new ArrayList<>();
/** 包装器 */
private Wrapper wrapper;
@ -284,14 +284,18 @@ public class SqlBuilder implements Builder<String>{
}
/**
* 添加Where语句所有逻辑之间为AND的关系
* 添加Where语句所有逻辑之间关系使用{@link Condition#setLinkOperator(LogicalOperator)} 定义
*
* @param conditions 条件当条件为空时只添加WHERE关键字
* @return 自己
* @since 4.4.4
*/
public SqlBuilder where(Condition... conditions) {
return where(LogicalOperator.AND, conditions);
if (ArrayUtil.isNotEmpty(conditions)) {
where(buildCondition(conditions));
}
return this;
}
/**
@ -301,17 +305,11 @@ public class SqlBuilder implements Builder<String>{
* @param logicalOperator 逻辑运算符
* @param conditions 条件当条件为空时只添加WHERE关键字
* @return 自己
* @deprecated logicalOperator放在Condition中了因此请使用 {@link #where(Condition...)}
*/
@Deprecated
public SqlBuilder where(LogicalOperator logicalOperator, Condition... conditions) {
if (ArrayUtil.isNotEmpty(conditions)) {
if (null != wrapper) {
// 包装字段名
conditions = wrapper.wrap(conditions);
}
where(buildCondition(logicalOperator, conditions));
}
return this;
return where(conditions);
}
/**
@ -366,14 +364,23 @@ public class SqlBuilder implements Builder<String>{
* @param logicalOperator 逻辑运算符
* @param conditions 条件
* @return 自己
* @deprecated logicalOperator放在Condition中了因此请使用 {@link #having(Condition...)}
*/
@Deprecated
public SqlBuilder having(LogicalOperator logicalOperator, Condition... conditions) {
return having(conditions);
}
/**
* 添加Having语句所有逻辑之间关系使用{@link Condition#setLinkOperator(LogicalOperator)} 定义
*
* @param conditions 条件
* @return this
* @since 5.4.3
*/
public SqlBuilder having(Condition... conditions) {
if (ArrayUtil.isNotEmpty(conditions)) {
if (null != wrapper) {
// 包装字段名
conditions = wrapper.wrap(conditions);
}
having(buildCondition(logicalOperator, conditions));
having(buildCondition(conditions));
}
return this;
@ -461,14 +468,23 @@ public class SqlBuilder implements Builder<String>{
* @param logicalOperator 逻辑运算符
* @param conditions 条件
* @return 自己
* @deprecated logicalOperator放在Condition中了因此请使用 {@link #on(Condition...)}
*/
@Deprecated
public SqlBuilder on(LogicalOperator logicalOperator, Condition... conditions) {
return on(conditions);
}
/**
* 配合JOIN的 ON语句多表关联的条件语句所有逻辑之间关系使用{@link Condition#setLinkOperator(LogicalOperator)} 定义
*
* @param conditions 条件
* @return this
* @since 5.4.3
*/
public SqlBuilder on(Condition... conditions) {
if (ArrayUtil.isNotEmpty(conditions)) {
if (null != wrapper) {
// 包装字段名
conditions = wrapper.wrap(conditions);
}
on(buildCondition(logicalOperator, conditions));
on(buildCondition(conditions));
}
return this;
@ -582,34 +598,20 @@ public class SqlBuilder implements Builder<String>{
* 构建组合条件<br>
* 例如name = ? AND type IN (?, ?) AND other LIKE ?
*
* @param logicalOperator 逻辑运算符
* @param conditions 条件对象
* @return 构建后的SQL语句条件部分
*/
private String buildCondition(LogicalOperator logicalOperator, Condition... conditions) {
private String buildCondition(Condition... conditions) {
if (ArrayUtil.isEmpty(conditions)) {
return StrUtil.EMPTY;
}
if (null == logicalOperator) {
logicalOperator = LogicalOperator.AND;
if (null != wrapper) {
// 包装字段名
conditions = wrapper.wrap(conditions);
}
final StringBuilder conditionStrBuilder = new StringBuilder();
boolean isFirst = true;
for (Condition condition : conditions) {
// 添加逻辑运算符
if (isFirst) {
isFirst = false;
} else {
// " AND " 或者 " OR "
conditionStrBuilder.append(StrUtil.SPACE).append(logicalOperator).append(StrUtil.SPACE);
}
// 构建条件部分"name = ?""name IN (?,?,?)""name BETWEEN AND ""name LIKE ?"
conditionStrBuilder.append(condition.toString(this.paramValues));
}
return conditionStrBuilder.toString();
return ConditionBuilder.of(conditions).build(this.paramValues);
}
/**

View File

@ -0,0 +1,21 @@
package cn.hutool.db.sql;
import org.junit.Assert;
import org.junit.Test;
public class ConditionBuilderTest {
@Test
public void buildTest(){
Condition c1 = new Condition("user", null);
Condition c2 = new Condition("name", "!= null");
c2.setLinkOperator(LogicalOperator.OR);
Condition c3 = new Condition("group", "like %aaa");
final ConditionBuilder builder = ConditionBuilder.of(c1, c2, c3);
final String sql = builder.build();
Assert.assertEquals("user IS NULL OR name IS NOT NULL AND group LIKE ?", sql);
Assert.assertEquals(1, builder.getParamValues().size());
Assert.assertEquals("%aaa", builder.getParamValues().get(0));
}
}