Browse Source

add ConditionBuilder

Looly 5 years ago
parent
commit
5a73a17f4d

+ 1 - 0
CHANGELOG.md

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

+ 3 - 0
hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java

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

+ 2 - 1
hutool-db/src/main/java/cn/hutool/db/AbstractDb.java

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

+ 3 - 4
hutool-db/src/main/java/cn/hutool/db/dialect/impl/AnsiSqlDialect.java

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

+ 25 - 0
hutool-db/src/main/java/cn/hutool/db/sql/Condition.java

@@ -69,6 +69,11 @@ public class Condition extends CloneSupport<Condition> {
 	private Object secondValue;
 	private Object secondValue;
 
 
 	/**
 	/**
+	 * 与前一个Condition连接的逻辑运算符,可以是and或or
+	 */
+	private LogicalOperator linkOperator = LogicalOperator.AND;
+
+	/**
 	 * 解析为Condition
 	 * 解析为Condition
 	 *
 	 *
 	 * @param field      字段名
 	 * @param field      字段名
@@ -282,6 +287,26 @@ public class Condition extends CloneSupport<Condition> {
 		this.secondValue = secondValue;
 		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
 	// --------------------------------------------------------------- Getters and Setters end
 
 
 	@Override
 	@Override

+ 116 - 0
hutool-db/src/main/java/cn/hutool/db/sql/ConditionBuilder.java

@@ -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();
+	}
+}

+ 45 - 43
hutool-db/src/main/java/cn/hutool/db/sql/SqlBuilder.java

@@ -67,11 +67,11 @@ public class SqlBuilder implements Builder<String>{
 	}
 	}
 	// --------------------------------------------------------------- Enums end
 	// --------------------------------------------------------------- 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;
 	private Wrapper wrapper;
 
 
@@ -284,14 +284,18 @@ public class SqlBuilder implements Builder<String>{
 	}
 	}
 	
 	
 	/**
 	/**
-	 * 添加Where语句,所有逻辑之间为AND的关系
+	 * 添加Where语句,所有逻辑之间关系使用{@link Condition#setLinkOperator(LogicalOperator)} 定义
 	 * 
 	 * 
 	 * @param conditions 条件,当条件为空时,只添加WHERE关键字
 	 * @param conditions 条件,当条件为空时,只添加WHERE关键字
 	 * @return 自己
 	 * @return 自己
 	 * @since 4.4.4
 	 * @since 4.4.4
 	 */
 	 */
 	public SqlBuilder where(Condition... conditions) {
 	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 logicalOperator 逻辑运算符
 	 * @param conditions 条件,当条件为空时,只添加WHERE关键字
 	 * @param conditions 条件,当条件为空时,只添加WHERE关键字
 	 * @return 自己
 	 * @return 自己
+	 * @deprecated logicalOperator放在Condition中了,因此请使用 {@link #where(Condition...)}
 	 */
 	 */
+	@Deprecated
 	public SqlBuilder where(LogicalOperator logicalOperator, Condition... conditions) {
 	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 logicalOperator 逻辑运算符
 	 * @param conditions 条件
 	 * @param conditions 条件
 	 * @return 自己
 	 * @return 自己
+	 * @deprecated logicalOperator放在Condition中了,因此请使用 {@link #having(Condition...)}
 	 */
 	 */
+	@Deprecated
 	public SqlBuilder having(LogicalOperator logicalOperator, Condition... conditions) {
 	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 (ArrayUtil.isNotEmpty(conditions)) {
-			if (null != wrapper) {
-				// 包装字段名
-				conditions = wrapper.wrap(conditions);
-			}
-			having(buildCondition(logicalOperator, conditions));
+			having(buildCondition(conditions));
 		}
 		}
 
 
 		return this;
 		return this;
@@ -461,14 +468,23 @@ public class SqlBuilder implements Builder<String>{
 	 * @param logicalOperator 逻辑运算符
 	 * @param logicalOperator 逻辑运算符
 	 * @param conditions 条件
 	 * @param conditions 条件
 	 * @return 自己
 	 * @return 自己
+	 * @deprecated logicalOperator放在Condition中了,因此请使用 {@link #on(Condition...)}
 	 */
 	 */
+	@Deprecated
 	public SqlBuilder on(LogicalOperator logicalOperator, Condition... conditions) {
 	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 (ArrayUtil.isNotEmpty(conditions)) {
-			if (null != wrapper) {
-				// 包装字段名
-				conditions = wrapper.wrap(conditions);
-			}
-			on(buildCondition(logicalOperator, conditions));
+			on(buildCondition(conditions));
 		}
 		}
 
 
 		return this;
 		return this;
@@ -582,34 +598,20 @@ public class SqlBuilder implements Builder<String>{
 	 * 构建组合条件<br>
 	 * 构建组合条件<br>
 	 * 例如:name = ? AND type IN (?, ?) AND other LIKE ?
 	 * 例如:name = ? AND type IN (?, ?) AND other LIKE ?
 	 * 
 	 * 
-	 * @param logicalOperator 逻辑运算符
 	 * @param conditions 条件对象
 	 * @param conditions 条件对象
 	 * @return 构建后的SQL语句条件部分
 	 * @return 构建后的SQL语句条件部分
 	 */
 	 */
-	private String buildCondition(LogicalOperator logicalOperator, Condition... conditions) {
+	private String buildCondition(Condition... conditions) {
 		if (ArrayUtil.isEmpty(conditions)) {
 		if (ArrayUtil.isEmpty(conditions)) {
 			return StrUtil.EMPTY;
 			return StrUtil.EMPTY;
 		}
 		}
-		if (null == logicalOperator) {
-			logicalOperator = LogicalOperator.AND;
-		}
-
-		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));
+		if (null != wrapper) {
+			// 包装字段名
+			conditions = wrapper.wrap(conditions);
 		}
 		}
 
 
-		return conditionStrBuilder.toString();
+		return ConditionBuilder.of(conditions).build(this.paramValues);
 	}
 	}
 
 
 	/**
 	/**

+ 21 - 0
hutool-db/src/test/java/cn/hutool/db/sql/ConditionBuilderTest.java

@@ -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));
+	}
+}