diff --git a/src/org/redkale/source/FilterColumn.java b/src/org/redkale/source/FilterColumn.java index dd6de780b..72f11b8f3 100644 --- a/src/org/redkale/source/FilterColumn.java +++ b/src/org/redkale/source/FilterColumn.java @@ -5,9 +5,9 @@ */ package org.redkale.source; -import java.lang.annotation.*; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; /** * @@ -46,4 +46,11 @@ public @interface FilterColumn { */ FilterExpress express() default FilterExpress.EQUAL; + /** + * 当标记的字段类型是数组/Collection类型且express不是IN/NOTIN时,则构建过滤条件时会遍历字段值的元素来循环构建表达式,元素之间的关系是AND或OR由该值来确定 + * + * @return 数组元素间的表达式是否AND关系 + */ + boolean itemand() default true; + } diff --git a/src/org/redkale/source/FilterJoinNode.java b/src/org/redkale/source/FilterJoinNode.java index 80a374896..3bfc9f916 100644 --- a/src/org/redkale/source/FilterJoinNode.java +++ b/src/org/redkale/source/FilterJoinNode.java @@ -5,15 +5,18 @@ */ package org.redkale.source; -import java.io.*; +import java.io.Serializable; import java.util.*; -import java.util.concurrent.atomic.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.*; -import org.redkale.util.*; +import static org.redkale.source.FilterExpress.EQUAL; +import org.redkale.util.Attribute; /** * - *

详情见: http://www.redkale.org + *

+ * 详情见: http://www.redkale.org + * * @author zhangjx */ public class FilterJoinNode extends FilterNode { @@ -27,41 +30,55 @@ public class FilterJoinNode extends FilterNode { public FilterJoinNode() { } - protected FilterJoinNode(Class joinClass, String[] joinColumns, String column, Serializable value) { - this(joinClass, joinColumns, column, null, value); - } - - protected FilterJoinNode(Class joinClass, String[] joinColumns, String column, FilterExpress express, Serializable value) { + protected FilterJoinNode(Class joinClass, String[] joinColumns, String column, FilterExpress express, boolean itemand, Serializable value) { Objects.requireNonNull(joinClass); Objects.requireNonNull(joinColumns); + if (express == null && value != null) { + if (value instanceof Range) { + express = FilterExpress.BETWEEN; + } else if (value instanceof Collection) { + express = FilterExpress.IN; + } else if (value.getClass().isArray()) { + express = FilterExpress.IN; + } + } this.joinClass = joinClass; this.joinColumns = joinColumns; this.column = column; - this.express = express; + this.express = express == null ? EQUAL : express; + this.itemand = itemand; this.value = value; } protected FilterJoinNode(FilterJoinNode node) { - this(node.joinClass, node.joinColumns, node.column, node.express, node.value); + this(node.joinClass, node.joinColumns, node.column, node.express, node.itemand, node.value); this.joinEntity = node.joinEntity; this.or = node.or; this.nodes = node.nodes; } public static FilterJoinNode create(Class joinClass, String joinColumn, String column, Serializable value) { - return new FilterJoinNode(joinClass, new String[]{joinColumn}, column, value); + return create(joinClass, new String[]{joinColumn}, column, value); } public static FilterJoinNode create(Class joinClass, String joinColumn, String column, FilterExpress express, Serializable value) { - return new FilterJoinNode(joinClass, new String[]{joinColumn}, column, express, value); + return create(joinClass, new String[]{joinColumn}, column, express, value); + } + + public static FilterJoinNode create(Class joinClass, String joinColumn, String column, FilterExpress express, boolean itemand, Serializable value) { + return create(joinClass, new String[]{joinColumn}, column, express, itemand, value); } public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, Serializable value) { - return new FilterJoinNode(joinClass, joinColumns, column, value); + return create(joinClass, joinColumns, column, null, value); } public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, FilterExpress express, Serializable value) { - return new FilterJoinNode(joinClass, joinColumns, column, express, value); + return create(joinClass, joinColumns, column, express, true, value); + } + + public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, FilterExpress express, boolean itemand, Serializable value) { + return new FilterJoinNode(joinClass, joinColumns, column, express, itemand, value); } @Override @@ -87,6 +104,7 @@ public class FilterJoinNode extends FilterNode { this.nodes = new FilterNode[]{new FilterJoinNode(node), node}; this.column = null; this.express = null; + this.itemand = true; this.value = null; this.joinClass = null; this.joinEntity = null; @@ -260,7 +278,7 @@ public class FilterJoinNode extends FilterNode { StringBuilder sb = new StringBuilder(); String[] joinColumns = node.joinColumns; sb.append(" INNER JOIN ").append(node.joinEntity.getTable()).append(" ").append(joinTabalis.get(node.joinClass)) - .append(" ON ").append(info.getSQLColumn("a", joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), joinColumns[0])); + .append(" ON ").append(info.getSQLColumn("a", joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), joinColumns[0])); for (int i = 1; i < joinColumns.length; i++) { sb.append(" AND ").append(info.getSQLColumn("a", joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), joinColumns[i])); } diff --git a/src/org/redkale/source/FilterNode.java b/src/org/redkale/source/FilterNode.java index fecb95135..9ba5311cd 100644 --- a/src/org/redkale/source/FilterNode.java +++ b/src/org/redkale/source/FilterNode.java @@ -28,6 +28,8 @@ public class FilterNode { protected Serializable value; + protected boolean itemand; + //---------------------------------------------- protected boolean or; @@ -36,21 +38,40 @@ public class FilterNode { public FilterNode() { } - protected FilterNode(String col, FilterExpress exp, Serializable val) { + protected FilterNode(String col, FilterExpress exp, boolean itemand, Serializable val) { Objects.requireNonNull(col); if (exp == null) { if (val instanceof Range) { exp = FilterExpress.BETWEEN; } else if (val instanceof Collection) { - exp = FilterExpress.IN; + if (!((Collection) val).isEmpty()) { + Object subval = null; + for (Object obj : (Collection) val) { //取第一个值 + subval = obj; + break; + } + if (subval instanceof Range) { + exp = FilterExpress.BETWEEN; + } else if (subval instanceof Collection) { + exp = FilterExpress.IN; + } else if (subval != null && val.getClass().isArray()) { + exp = FilterExpress.IN; + } + } else { //空集合 + exp = FilterExpress.IN; + } } else if (val != null && val.getClass().isArray()) { - exp = FilterExpress.IN; - } else { - exp = FilterExpress.EQUAL; + Class comp = val.getClass().getComponentType(); + if (Range.class.isAssignableFrom(comp)) { + exp = FilterExpress.BETWEEN; + } else if (comp.isArray() || Collection.class.isAssignableFrom(comp)) { + exp = FilterExpress.IN; + } } } this.column = col; - this.express = exp; + this.express = exp == null ? EQUAL : exp; + this.itemand = itemand; this.value = val; } @@ -63,7 +84,11 @@ public class FilterNode { } public final FilterNode and(String column, FilterExpress express, Serializable value) { - return and(new FilterNode(column, express, value)); + return and(column, express, true, value); + } + + public final FilterNode and(String column, FilterExpress express, boolean itemand, Serializable value) { + return and(new FilterNode(column, express, itemand, value)); } public final FilterNode or(FilterNode node) { @@ -75,7 +100,11 @@ public class FilterNode { } public final FilterNode or(String column, FilterExpress express, Serializable value) { - return or(new FilterNode(column, express, value)); + return or(column, express, true, value); + } + + public final FilterNode or(String column, FilterExpress express, boolean itemand, Serializable value) { + return or(new FilterNode(column, express, itemand, value)); } protected FilterNode any(FilterNode node, boolean signor) { @@ -83,6 +112,7 @@ public class FilterNode { if (this.column == null) { this.column = node.column; this.express = node.express; + this.itemand = node.itemand; this.value = node.value; return this; } @@ -98,12 +128,13 @@ public class FilterNode { this.nodes = newsiblings; return this; } - FilterNode newnode = new FilterNode(this.column, this.express, this.value); + FilterNode newnode = new FilterNode(this.column, this.express, this.itemand, this.value); newnode.or = this.or; newnode.nodes = this.nodes; this.nodes = new FilterNode[]{newnode, node}; this.column = null; this.express = null; + this.itemand = true; this.or = signor; this.value = null; return this; @@ -206,16 +237,81 @@ public class FilterNode { } public static FilterNode create(String column, FilterExpress express, Serializable value) { - return new FilterNode(column, express, value); + return create(column, express, true, value); + } + + public static FilterNode create(String column, FilterExpress express, boolean itemand, Serializable value) { + return new FilterNode(column, express, itemand, value); + } + + private boolean needSplit(final Object val0) { + return needSplit(express, val0); + } + + private static boolean needSplit(final FilterExpress express, final Object val0) { + boolean items = express != IN && express != NOTIN; //是否数组集合的表达式 + if (!items) { + if (val0.getClass().isArray()) { + Class comp = val0.getClass().getComponentType(); + if (!(comp.isPrimitive() || CharSequence.class.isAssignableFrom(comp) || Number.class.isAssignableFrom(comp))) { + items = true; + } + } else if (val0 instanceof Collection) { + for (Object fv : (Collection) val0) { + if (fv == null) continue; + Class comp = fv.getClass(); + if (!(comp.isPrimitive() || CharSequence.class.isAssignableFrom(comp) || Number.class.isAssignableFrom(comp))) { + items = true; + } + break; //只需检测第一个值 + } + } + } + return items; } protected final CharSequence createElementSQLExpress(final EntityInfo info, String talis) { + final Object val0 = getValue(); + if (needSplit(val0)) { + if (val0 instanceof Collection) { + StringBuilder sb = new StringBuilder(); + boolean more = ((Collection) val0).size() > 1; + if (more) sb.append('('); + for (Object fv : (Collection) val0) { + if (fv == null) continue; + CharSequence cs = createElementSQLExpress(info, talis, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } else if (val0.getClass().isArray()) { + StringBuilder sb = new StringBuilder(); + Object[] fvs = (Object[]) val0; + boolean more = fvs.length > 1; + if (more) sb.append('('); + for (Object fv : fvs) { + if (fv == null) continue; + CharSequence cs = createElementSQLExpress(info, talis, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } + } + return createElementSQLExpress(info, talis, val0); + + } + + private CharSequence createElementSQLExpress(final EntityInfo info, String talis, Object val0) { if (column == null) return null; if (talis == null) talis = "a"; if (express == ISNULL || express == ISNOTNULL) { return new StringBuilder().append(info.getSQLColumn(talis, column)).append(' ').append(express.value()); } - Object val0 = getValue(); if (val0 == null) return null; if (express == FV_MOD || express == FV_DIV) { FilterValue fv = (FilterValue) val0; @@ -294,6 +390,81 @@ public class FilterNode { @SuppressWarnings("unchecked") protected final Predicate createElementPredicate(final EntityCache cache, final boolean join, final Attribute attr) { + final Object val0 = getValue(); + if (needSplit(val0)) { + if (val0 instanceof Collection) { + Predicate filter = null; + for (Object fv : (Collection) val0) { + if (fv == null) continue; + Predicate f = createElementPredicate(cache, join, attr, fv); + if (f == null) continue; + final Predicate one = filter; + final Predicate two = f; + filter = (filter == null) ? f : (!itemand ? new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) || two.test(t); + } + + @Override + public String toString() { + return "(" + one + " OR " + two + ")"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) && two.test(t); + } + + @Override + public String toString() { + return "(" + one + " AND " + two + ")"; + } + }); + } + return filter; + } else if (val0.getClass().isArray()) { + Predicate filter = null; + for (Object fv : (Object[]) val0) { + if (fv == null) continue; + Predicate f = createElementPredicate(cache, join, attr, fv); + if (f == null) continue; + final Predicate one = filter; + final Predicate two = f; + filter = (filter == null) ? f : (!itemand ? new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) || two.test(t); + } + + @Override + public String toString() { + return "(" + one + " OR " + two + ")"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) && two.test(t); + } + + @Override + public String toString() { + return "(" + one + " AND " + two + ")"; + } + }); + } + return filter; + } + } + return createElementPredicate(cache, join, attr, val0); + } + + @SuppressWarnings("unchecked") + protected final Predicate createElementPredicate(final EntityCache cache, final boolean join, final Attribute attr, Object val0) { if (attr == null) return null; final String field = join ? (cache.getType().getSimpleName() + "." + attr.field()) : attr.field(); if (express == ISNULL) return new Predicate() { @@ -321,7 +492,6 @@ public class FilterNode { } }; if (attr == null) return null; - Serializable val0 = getValue(); if (val0 == null) return null; final Class atype = attr.type(); @@ -436,7 +606,7 @@ public class FilterNode { } } } - final Serializable val = val0; + final Serializable val = (Serializable) val0; switch (express) { case EQUAL: return new Predicate() { @@ -1125,10 +1295,44 @@ public class FilterNode { } protected final StringBuilder toElementString(final String prefix) { + Serializable val0 = getValue(); + if (needSplit(val0)) { + if (val0 instanceof Collection) { + StringBuilder sb = new StringBuilder(); + boolean more = ((Collection) val0).size() > 1; + if (more) sb.append('('); + for (Object fv : (Collection) val0) { + if (fv == null) continue; + CharSequence cs = toElementString(prefix, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } else if (val0.getClass().isArray()) { + StringBuilder sb = new StringBuilder(); + Object[] fvs = (Object[]) val0; + boolean more = fvs.length > 1; + if (more) sb.append('('); + for (Object fv : fvs) { + if (fv == null) continue; + CharSequence cs = toElementString(prefix, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } + } + return toElementString(prefix, val0); + } + + protected final StringBuilder toElementString(final String prefix, Object ev) { StringBuilder sb = new StringBuilder(); if (column != null) { String col = prefix == null ? column : (prefix + "." + column); - Serializable ev = getValue(); if (express == ISNULL || express == ISNOTNULL) { sb.append(col).append(' ').append(express.value()); } else if (ev != null) { @@ -1245,6 +1449,14 @@ public class FilterNode { this.express = express; } + public final boolean isItemand() { + return itemand; + } + + public final void setItemand(boolean itemand) { + this.itemand = itemand; + } + public final FilterNode[] getNodes() { return nodes; } diff --git a/src/org/redkale/source/FilterNodeBean.java b/src/org/redkale/source/FilterNodeBean.java index 277c41741..75f8d7af4 100644 --- a/src/org/redkale/source/FilterNodeBean.java +++ b/src/org/redkale/source/FilterNodeBean.java @@ -5,13 +5,13 @@ */ package org.redkale.source; -import java.io.*; +import java.io.Serializable; import java.lang.reflect.*; import java.util.*; -import java.util.concurrent.*; -import javax.persistence.*; +import java.util.concurrent.ConcurrentHashMap; +import javax.persistence.Transient; import static org.redkale.source.FilterExpress.*; -import org.redkale.util.*; +import org.redkale.util.Attribute; /** * @@ -19,8 +19,9 @@ import org.redkale.util.*; * 详情见: http://www.redkale.org * * @author zhangjx + * @param FilterBean泛型 */ -public final class FilterNodeBean implements Comparable> { +public final class FilterNodeBean implements Comparable> { private static final ConcurrentHashMap beanodes = new ConcurrentHashMap<>(); @@ -30,6 +31,8 @@ public final class FilterNodeBean implements Comparable> { private FilterExpress express; + private boolean itemand; + private boolean or; private FilterNodeBean[] nodeBeans; @@ -50,6 +53,7 @@ public final class FilterNodeBean implements Comparable> { this.beanAttr = bean == null ? null : bean.beanAttr; this.column = bean == null ? null : bean.column; this.express = bean == null ? null : bean.express; + this.itemand = bean == null ? true : bean.itemand; this.joinClass = bean == null ? null : bean.joinClass; this.joinColumns = bean == null ? null : bean.joinColumns; this.least = bean == null ? 1 : bean.least; @@ -68,10 +72,12 @@ public final class FilterNodeBean implements Comparable> { this.column = (filterCol != null && !filterCol.name().isEmpty()) ? filterCol.name() : attr.field(); FilterExpress exp = filterCol == null ? null : filterCol.express(); - if (type.isArray() || Collection.class.isAssignableFrom(type)) { + if ((exp == null || exp == EQUAL) && (type.isArray() || Collection.class.isAssignableFrom(type))) { if (Range.class.isAssignableFrom(type.getComponentType())) { if (AND != exp) exp = OR; - } else if (NOTIN != exp) exp = IN; + } else if (NOTIN != exp) { + exp = IN; + } } else if (Range.class.isAssignableFrom(type)) { if (NOTBETWEEN != exp) exp = BETWEEN; } @@ -97,6 +103,7 @@ public final class FilterNodeBean implements Comparable> { this.beanAttr = node.beanAttr; this.column = node.column; this.express = node.express; + this.itemand = node.itemand; this.joinClass = node.joinClass; this.joinColumns = node.joinColumns; this.least = node.least; @@ -133,16 +140,16 @@ public final class FilterNodeBean implements Comparable> { final Serializable val = beanAttr.get(bean); if (column != null && val != null) { boolean skip = false; - if (string && ((CharSequence) val).length() == 0) { + if (string && ((CharSequence) val).length() == 0) { //空字符串不需要进行过滤 skip = true; - } else if (number && ((Number) val).longValue() < least) { + } else if (number && ((Number) val).longValue() < least) { //数值小于过滤下值限则不需要过滤 skip = true; } if (!skip) { if (this.joinClass == null) { - node = FilterNode.create(column, express, val); + node = FilterNode.create(column, express, itemand, val); } else { - node = FilterJoinNode.create(joinClass, joinColumns, column, express, val); + node = FilterJoinNode.create(joinClass, joinColumns, column, express, itemand, val); } } } @@ -221,7 +228,7 @@ public final class FilterNodeBean implements Comparable> { if (f == null) continue; rs = rs == null ? f : rs.and(f); } - if (rs !=null && rs.nodeBeans != null) Arrays.sort(rs.nodeBeans); + if (rs != null && rs.nodeBeans != null) Arrays.sort(rs.nodeBeans); return rs == null ? new FilterNodeBean(null) : rs; }