mirror of
https://gitee.com/dromara/hutool.git
synced 2025-05-05 05:07:58 +08:00
add ConditionBuilder
This commit is contained in:
parent
75a6da5b5f
commit
5a73a17f4d
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
116
hutool-db/src/main/java/cn/hutool/db/sql/ConditionBuilder.java
Normal file
116
hutool-db/src/main/java/cn/hutool/db/sql/ConditionBuilder.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user