diff --git a/src/com/wentch/redkale/source/DataDefaultSource.java b/src/com/wentch/redkale/source/DataDefaultSource.java index 5f45861c6..25abb32d7 100644 --- a/src/com/wentch/redkale/source/DataDefaultSource.java +++ b/src/com/wentch/redkale/source/DataDefaultSource.java @@ -641,7 +641,9 @@ public final class DataDefaultSource implements DataSource, Nameable { private void delete(final Connection conn, final EntityInfo info, final FilterNode node) { try { if (!info.isVirtualEntity()) { - String sql = "DELETE FROM " + info.getTable() + node.createFilterSQLExpress(info, null); + CharSequence join = node.createSQLJoin(info); + CharSequence where = node.createSQLExpress(info, null); + String sql = "DELETE FROM " + info.getTable() + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); if (debug.get()) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql); final Statement stmt = conn.createStatement(); stmt.execute(sql); @@ -1081,12 +1083,14 @@ public final class DataDefaultSource implements DataSource, Nameable { if (node == null && bean != null) node = loadFilterBeanNode(bean.getClass()); final EntityCache cache = info.getCache(); if (cache != null && (info.isVirtualEntity() || cache.isFullLoaded())) { - if (node == null || node.isJoinAllCached()) { + if (node == null || node.isCacheUseable()) { return cache.getNumberResult(reckon, column, node, bean); } } + final CharSequence join = node == null ? null : node.createSQLJoin(info); + final CharSequence where = node == null ? null : node.createSQLExpress(info, bean); final String sql = "SELECT " + reckon.getColumn((column == null || column.isEmpty() ? "*" : ("a." + column))) + " FROM " + info.getTable() + " a" - + (node == null ? "" : node.createFilterSQLExpress(info, bean)); + + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql); final PreparedStatement prestmt = conn.prepareStatement(sql); Number rs = null; @@ -1127,13 +1131,15 @@ public final class DataDefaultSource implements DataSource, Nameable { if (node == null && bean != null) node = loadFilterBeanNode(bean.getClass()); final EntityCache cache = info.getCache(); if (cache != null && (info.isVirtualEntity() || cache.isFullLoaded())) { - if (node == null || node.isJoinAllCached()) { + if (node == null || node.isCacheUseable()) { return cache.getMapResult(keyColumn, reckon, reckonColumn, node, bean); } } final String sqlkey = info.getSQLColumn(keyColumn); + final CharSequence join = node == null ? null : node.createSQLJoin(info); + final CharSequence where = node == null ? null : node.createSQLExpress(info, bean); final String sql = "SELECT a." + sqlkey + ", " + reckon.getColumn((reckonColumn == null || reckonColumn.isEmpty() ? "*" : ("a." + reckonColumn))) - + " FROM " + info.getTable() + " a" + (node == null ? "" : node.createFilterSQLExpress(info, bean)) + " GROUP BY a." + sqlkey; + + " FROM " + info.getTable() + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey; if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql); final PreparedStatement prestmt = conn.prepareStatement(sql); Map rs = new LinkedHashMap<>(); @@ -1433,8 +1439,8 @@ public final class DataDefaultSource implements DataSource, Nameable { final EntityCache cache = info.getCache(); if (node == null && bean != null) node = loadFilterBeanNode(bean.getClass()); if (readcache && cache != null) { - if (node == null || node.isJoinAllCached()) { - if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : node.createFilterPredicate(info, bean))); + if (node == null || node.isCacheUseable()) { + if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : node.createPredicate(cache, bean))); Sheet sheet = cache.querySheet(needtotal, selects, flipper, node, bean); if (!sheet.isEmpty() || info.isVirtualEntity() || cache.isFullLoaded()) return sheet; } @@ -1443,8 +1449,10 @@ public final class DataDefaultSource implements DataSource, Nameable { try { final SelectColumn sels = selects; final List list = new ArrayList(); - final String sql = "SELECT a.* FROM " + info.getTable() + " a" - + (node == null ? "" : node.createFilterSQLExpress(info, bean)) + createFilterSQLOrderBy(info, flipper); + final CharSequence join = node == null ? null : node.createSQLJoin(info); + final CharSequence where = node == null ? null : node.createSQLExpress(info, bean); + final String sql = "SELECT a.* FROM " + info.getTable() + " a" + (join == null ? "" : join) + + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + info.createSQLOrderby(flipper); if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " query sql=" + sql + (flipper == null ? "" : (" LIMIT " + flipper.index() + "," + flipper.getSize()))); final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); diff --git a/src/com/wentch/redkale/source/EntityCache.java b/src/com/wentch/redkale/source/EntityCache.java index 1a88e2ec9..53f93ee62 100644 --- a/src/com/wentch/redkale/source/EntityCache.java +++ b/src/com/wentch/redkale/source/EntityCache.java @@ -25,81 +25,11 @@ import java.util.stream.*; */ public final class EntityCache { - private static class UniqueSequence implements Serializable { - - private final Serializable[] value; - - public UniqueSequence(Serializable[] val) { - this.value = val; - } - - @Override - public int hashCode() { - return Arrays.deepHashCode(this.value); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final UniqueSequence other = (UniqueSequence) obj; - if (value.length != other.value.length) return false; - for (int i = 0; i < value.length; i++) { - if (!value[i].equals(other.value[i])) return false; - } - return true; - } - - } - - private static interface UniqueAttribute extends Predicate { - - public Serializable getValue(T bean); - - @Override - public boolean test(FilterNode node); - - public static UniqueAttribute create(final Attribute[] attributes) { - if (attributes.length == 1) { - final Attribute attribute = attributes[0]; - return new UniqueAttribute() { - - @Override - public Serializable getValue(T bean) { - return attribute.get(bean); - } - - @Override - public boolean test(FilterNode node) { - return true; - } - }; - } else { - return new UniqueAttribute() { - - @Override - public Serializable getValue(T bean) { - final Serializable[] rs = new Serializable[attributes.length]; - for (int i = 0; i < rs.length; i++) { - rs[i] = attributes[i].get(bean); - } - return new UniqueSequence(rs); - } - - @Override - public boolean test(FilterNode node) { - return true; - } - }; - } - } - } - private static final Logger logger = Logger.getLogger(EntityCache.class.getName()); private final ConcurrentHashMap map = new ConcurrentHashMap(); - private final CopyOnWriteArrayList list = new CopyOnWriteArrayList(); // CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%; + private final Collection list = new CopyOnWriteArrayList(); // CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%; private final HashMap, ConcurrentHashMap>> uniques = new HashMap<>(); @@ -180,14 +110,19 @@ public final class EntityCache { } public boolean exists(final Predicate filter) { - return (filter != null) && listStream().filter(filter).findFirst().isPresent(); + return (filter != null) && this.list.stream().filter(filter).findFirst().isPresent(); } - public Map getMapResult(final String keyColumn, final Reckon reckon, final String reckonColumn, final FilterNode node, final FilterBean bean) { + public Map getMapResult(final String keyColumn, final Reckon reckon, final String reckonColumn, final FilterNode node) { + return getMapResult(keyColumn, reckon, reckonColumn, node, null); + } + + public Map getMapResult(final String keyColumn, final Reckon reckon, final String reckonColumn, FilterNode node, final FilterBean bean) { + if (node == null && bean != null) node = FilterBeanNode.load(bean.getClass()); final Attribute keyAttr = info.getAttribute(keyColumn); - final Predicate filter = node == null ? null : node.createFilterPredicate(this.info, bean); + final Predicate filter = node == null ? null : node.createPredicate(this, bean); final Attribute reckonAttr = reckonColumn == null ? null : info.getAttribute(reckonColumn); - Stream stream = listStream(); + Stream stream = this.list.stream(); if (filter != null) stream = stream.filter(filter); Collector collector = null; final Class valtype = reckonAttr == null ? null : reckonAttr.type(); @@ -232,10 +167,15 @@ public final class EntityCache { return rs; } - public Number getNumberResult(final Reckon reckon, final String column, final FilterNode node, final FilterBean bean) { + public Number getNumberResult(final Reckon reckon, final String column, final FilterNode node) { + return getNumberResult(reckon, column, node, null); + } + + public Number getNumberResult(final Reckon reckon, final String column, FilterNode node, final FilterBean bean) { + if (node == null && bean != null) node = FilterBeanNode.load(bean.getClass()); final Attribute attr = column == null ? null : info.getAttribute(column); - final Predicate filter = node == null ? null : node.createFilterPredicate(this.info, bean); - Stream stream = listStream(); + final Predicate filter = node == null ? null : node.createPredicate(this, bean); + Stream stream = this.list.stream(); if (filter != null) stream = stream.filter(filter); switch (reckon) { case AVG: @@ -299,21 +239,30 @@ public final class EntityCache { return -1; } + public Sheet querySheet(final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheet(selects, flipper, node, null); + } + public Sheet querySheet(final SelectColumn selects, final Flipper flipper, final FilterNode node, final FilterBean bean) { return querySheet(true, selects, flipper, node, bean); } - public Sheet querySheet(final boolean needtotal, final SelectColumn selects, final Flipper flipper, final FilterNode node, final FilterBean bean) { - final Predicate filter = node == null ? null : node.createFilterPredicate(this.info, bean); - final Comparator comparator = FilterNode.createFilterComparator(this, flipper); + public Sheet querySheet(final boolean needtotal, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheet(needtotal, selects, flipper, node, null); + } + + public Sheet querySheet(final boolean needtotal, final SelectColumn selects, final Flipper flipper, FilterNode node, final FilterBean bean) { + if (node == null && bean != null) node = FilterBeanNode.load(bean.getClass()); + final Predicate filter = node == null ? null : node.createPredicate(this, bean); + final Comparator comparator = createComparator(flipper); long total = 0; if (needtotal) { - Stream stream = listStream(); + Stream stream = this.list.stream(); if (filter != null) stream = stream.filter(filter); total = stream.count(); } if (needtotal && total == 0) return new Sheet<>(); - Stream stream = listStream(); + Stream stream = this.list.stream(); if (filter != null) stream = stream.filter(filter); if (comparator != null) stream = stream.sorted(comparator); if (flipper != null) stream = stream.skip(flipper.index()).limit(flipper.getSize()); @@ -327,9 +276,9 @@ public final class EntityCache { } } else { final List> attrs = new ArrayList<>(); - for (Map.Entry> en : info.attributes.entrySet()) { - if (selects.validate(en.getKey())) attrs.add(en.getValue()); - } + info.forEachAttribute((k, v) -> { + if (selects.validate(k)) attrs.add(v); + }); Consumer action = x -> { final T item = creator.create(); for (Attribute attr : attrs) { @@ -371,7 +320,7 @@ public final class EntityCache { public Serializable[] delete(final FilterNode node) { if (node == null || this.list.isEmpty()) return new Serializable[0]; - Object[] rms = listStream().filter(node.createFilterPredicate(this.info, null)).toArray(); + Object[] rms = this.list.stream().filter(node.createPredicate(this, null)).toArray(); Serializable[] ids = new Serializable[rms.length]; int i = -1; for (Object o : rms) { @@ -453,15 +402,237 @@ public final class EntityCache { return rs; } - private Stream listStream() { - return this.list.stream(); + public Attribute getAttribute(String fieldname) { + return info.getAttribute(fieldname); } - protected Comparator getSortComparator(String sort) { - return this.sortComparators.get(sort); - } + //------------------------------------------------------------------------------------------------------------------------------- + protected Comparator createComparator(Flipper flipper) { + if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty()) return null; + final String sort = flipper.getSort(); + Comparator comparator = this.sortComparators.get(sort); + if (comparator != null) return comparator; + for (String item : sort.split(",")) { + if (item.trim().isEmpty()) continue; + String[] sub = item.trim().split("\\s+"); + int pos = sub[0].indexOf('('); + Attribute attr; + if (pos <= 0) { + attr = getAttribute(sub[0]); + } else { //含SQL函数 + int pos2 = sub[0].lastIndexOf(')'); + final Attribute pattr = getAttribute(sub[0].substring(pos + 1, pos2)); + final String func = sub[0].substring(0, pos); + if ("ABS".equalsIgnoreCase(func)) { + if (pattr.type() == int.class || pattr.type() == Integer.class) { + attr = new Attribute() { - protected void putSortComparator(String sort, Comparator comparator) { + @Override + public Class type() { + return pattr.type(); + } + + @Override + public Class declaringClass() { + return pattr.declaringClass(); + } + + @Override + public String field() { + return pattr.field(); + } + + @Override + public Serializable get(T obj) { + return Math.abs(((Number) pattr.get(obj)).intValue()); + } + + @Override + public void set(T obj, Serializable value) { + pattr.set(obj, value); + } + }; + } else if (pattr.type() == long.class || pattr.type() == Long.class) { + attr = new Attribute() { + + @Override + public Class type() { + return pattr.type(); + } + + @Override + public Class declaringClass() { + return pattr.declaringClass(); + } + + @Override + public String field() { + return pattr.field(); + } + + @Override + public Serializable get(T obj) { + return Math.abs(((Number) pattr.get(obj)).longValue()); + } + + @Override + public void set(T obj, Serializable value) { + pattr.set(obj, value); + } + }; + } else if (pattr.type() == float.class || pattr.type() == Float.class) { + attr = new Attribute() { + + @Override + public Class type() { + return pattr.type(); + } + + @Override + public Class declaringClass() { + return pattr.declaringClass(); + } + + @Override + public String field() { + return pattr.field(); + } + + @Override + public Serializable get(T obj) { + return Math.abs(((Number) pattr.get(obj)).floatValue()); + } + + @Override + public void set(T obj, Serializable value) { + pattr.set(obj, value); + } + }; + } else if (pattr.type() == double.class || pattr.type() == Double.class) { + attr = new Attribute() { + + @Override + public Class type() { + return pattr.type(); + } + + @Override + public Class declaringClass() { + return pattr.declaringClass(); + } + + @Override + public String field() { + return pattr.field(); + } + + @Override + public Serializable get(T obj) { + return Math.abs(((Number) pattr.get(obj)).doubleValue()); + } + + @Override + public void set(T obj, Serializable value) { + pattr.set(obj, value); + } + }; + } else { + throw new RuntimeException("Flipper not supported sort illegal type by ABS (" + flipper.getSort() + ")"); + } + } else if (func.isEmpty()) { + attr = pattr; + } else { + throw new RuntimeException("Flipper not supported sort illegal function (" + flipper.getSort() + ")"); + } + } + Comparator c = (sub.length > 1 && sub[1].equalsIgnoreCase("DESC")) ? (T o1, T o2) -> { + Comparable c1 = (Comparable) attr.get(o1); + Comparable c2 = (Comparable) attr.get(o2); + return c2 == null ? -1 : c2.compareTo(c1); + } : (T o1, T o2) -> { + Comparable c1 = (Comparable) attr.get(o1); + Comparable c2 = (Comparable) attr.get(o2); + return c1 == null ? -1 : c1.compareTo(c2); + }; + + if (comparator == null) { + comparator = c; + } else { + comparator = comparator.thenComparing(c); + } + } this.sortComparators.put(sort, comparator); + return comparator; } + + private static class UniqueSequence implements Serializable { + + private final Serializable[] value; + + public UniqueSequence(Serializable[] val) { + this.value = val; + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(this.value); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final UniqueSequence other = (UniqueSequence) obj; + if (value.length != other.value.length) return false; + for (int i = 0; i < value.length; i++) { + if (!value[i].equals(other.value[i])) return false; + } + return true; + } + + } + + private static interface UniqueAttribute extends Predicate { + + public Serializable getValue(T bean); + + @Override + public boolean test(FilterNode node); + + public static UniqueAttribute create(final Attribute[] attributes) { + if (attributes.length == 1) { + final Attribute attribute = attributes[0]; + return new UniqueAttribute() { + + @Override + public Serializable getValue(T bean) { + return attribute.get(bean); + } + + @Override + public boolean test(FilterNode node) { + return true; + } + }; + } else { + return new UniqueAttribute() { + + @Override + public Serializable getValue(T bean) { + final Serializable[] rs = new Serializable[attributes.length]; + for (int i = 0; i < rs.length; i++) { + rs[i] = attributes[i].get(bean); + } + return new UniqueSequence(rs); + } + + @Override + public boolean test(FilterNode node) { + return true; + } + }; + } + } + } + } diff --git a/src/com/wentch/redkale/source/EntityInfo.java b/src/com/wentch/redkale/source/EntityInfo.java index 6370abde8..fecbf5e36 100644 --- a/src/com/wentch/redkale/source/EntityInfo.java +++ b/src/com/wentch/redkale/source/EntityInfo.java @@ -44,7 +44,7 @@ public final class EntityInfo { //key是field的name, 不是sql字段。 //存放所有与数据库对应的字段, 包括主键 - final Map> attributes = new HashMap<>(); + private final HashMap> attributes = new HashMap<>(); //key是field的name, value是Column的别名,即数据库表的字段名 //只有field.name 与 Column.name不同才存放在aliasmap里. @@ -93,10 +93,12 @@ public final class EntityInfo { synchronized (entityInfos) { rs = entityInfos.get(clazz); if (rs == null) { + if (nodeid < 0) throw new IllegalArgumentException("nodeid(" + nodeid + ") is illegal"); rs = new EntityInfo(clazz, nodeid, cacheForbidden); entityInfos.put(clazz, rs); AutoLoad auto = clazz.getAnnotation(AutoLoad.class); - if (rs.cache != null && auto != null && auto.value() && fullloader != null) { + if (rs.cache != null && auto != null && auto.value()) { + if (fullloader == null) throw new IllegalArgumentException(clazz.getName() + " auto loader is illegal"); rs.cache.fullLoad(fullloader.apply(clazz)); } } @@ -107,7 +109,7 @@ public final class EntityInfo { private EntityInfo(Class type, int nodeid, final boolean cacheForbidden) { this.type = type; //--------------------------------------------- - this.nodeid = nodeid; + this.nodeid = nodeid >= 0 ? nodeid : 0; DistributeGenerator.DistributeTables dt = type.getAnnotation(DistributeGenerator.DistributeTables.class); this.distributeTables = dt == null ? null : dt.value(); @@ -267,7 +269,12 @@ public final class EntityInfo { return this.primary; } + public void forEachAttribute(BiConsumer> action) { + this.attributes.forEach(action); + } + public Attribute getAttribute(String fieldname) { + if (fieldname == null) return null; return this.attributes.get(fieldname); } @@ -279,12 +286,32 @@ public final class EntityInfo { return this.aliasmap == null; } - public String getSortOrderbySql(String sort) { - return this.sortOrderbySqls.get(sort); - } - - protected void putSortOrderbySql(String sort, String sql) { + protected String createSQLOrderby(Flipper flipper) { + if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty()) return ""; + final String sort = flipper.getSort(); + String sql = this.sortOrderbySqls.get(sort); + if (sql != null) return sql; + final StringBuilder sb = new StringBuilder(); + sb.append(" ORDER BY "); + if (isNoAlias()) { + sb.append(sort); + } else { + boolean flag = false; + for (String item : sort.split(",")) { + if (item.isEmpty()) continue; + String[] sub = item.split("\\s+"); + if (flag) sb.append(','); + if (sub.length < 2 || sub[1].equalsIgnoreCase("ASC")) { + sb.append("a.").append(getSQLColumn(sub[0])).append(" ASC"); + } else { + sb.append("a.").append(getSQLColumn(sub[0])).append(" DESC"); + } + flag = true; + } + } + sql = sb.toString(); this.sortOrderbySqls.put(sort, sql); + return sql; } //根据field字段名获取数据库对应的字段名 diff --git a/src/com/wentch/redkale/source/FilterBeanNode.java b/src/com/wentch/redkale/source/FilterBeanNode.java index 7bd582e6e..8b1a8ac79 100644 --- a/src/com/wentch/redkale/source/FilterBeanNode.java +++ b/src/com/wentch/redkale/source/FilterBeanNode.java @@ -26,6 +26,10 @@ final class FilterBeanNode extends FilterNode { private static final ConcurrentHashMap beanodes = new ConcurrentHashMap<>(); + public static FilterBeanNode load(Class clazz) { + return load(clazz, -1, true, null); + } + public static FilterBeanNode load(Class clazz, final int nodeid, final boolean cacheForbidden, Function fullloader) { FilterBeanNode rs = beanodes.get(clazz); @@ -55,17 +59,19 @@ final class FilterBeanNode extends FilterNode { if (field.getAnnotation(Ignore.class) != null) continue; if (field.getAnnotation(Transient.class) != null) continue; + final boolean pubmod = Modifier.isPublic(field.getModifiers()); + char[] chars = field.getName().toCharArray(); chars[0] = Character.toUpperCase(chars[0]); final Class t = field.getType(); - Method getter; + Method getter = null; try { getter = cltmp.getMethod(((t == boolean.class || t == Boolean.class) ? "is" : "get") + new String(chars)); } catch (Exception ex) { - continue; + if (!pubmod) continue; } fields.add(field.getName()); - FilterBeanNode newnode = new FilterBeanNode(field.getName(), true, Attribute.create(getter, null)); + FilterBeanNode newnode = new FilterBeanNode(field.getName(), true, pubmod ? Attribute.create(field) : Attribute.create(getter, null)); newnode.setField(field); //------------------------------------ { @@ -163,7 +169,7 @@ final class FilterBeanNode extends FilterNode { protected FilterBeanNode(String col, boolean sign, Attribute beanAttr) { this.column = col; - this.signand = sign; + this.and = sign; this.beanAttribute = beanAttr; } @@ -195,7 +201,7 @@ final class FilterBeanNode extends FilterNode { @Override protected void append(FilterNode node, boolean sign) { - FilterBeanNode newnode = new FilterBeanNode(this.column, this.signand, this.beanAttribute); + FilterBeanNode newnode = new FilterBeanNode(this.column, this.and, this.beanAttribute); newnode.express = this.express; newnode.nodes = this.nodes; newnode.foreignEntity = this.foreignEntity; @@ -211,7 +217,7 @@ final class FilterBeanNode extends FilterNode { this.nodes = new FilterNode[]{newnode}; this.column = node.column; this.express = node.express; - this.signand = sign; + this.and = sign; this.setValue(node.getValue()); if (node instanceof FilterBeanNode) { FilterBeanNode beanNode = ((FilterBeanNode) node); @@ -230,29 +236,27 @@ final class FilterBeanNode extends FilterNode { } @Override - protected StringBuilder createFilterSQLExpress(final boolean first, final EntityInfo info, FilterBean bean) { - if (joinSQL == null || !first) return super.createFilterSQLExpress(first, info, bean); - StringBuilder sb = super.createFilterSQLExpress(first, info, bean); - if (joinSQL == null) return sb; - return new StringBuilder(sb.length() + joinSQL.length()).append(joinSQL).append(sb); + protected CharSequence createSQLJoin(final EntityInfo info) { + if (joinSQL == null) return null; + return new StringBuilder(joinSQL); } + @Override - protected Predicate createFilterPredicate(final EntityInfo info, FilterBean bean) { - //if ((this.joinSQL == null && first) || this.foreignEntity == null) return super.createFilterPredicate(info, bean); - if (this.foreignEntity == null) return super.createFilterPredicate(info, bean); + protected Predicate createPredicate(final EntityCache cache, FilterBean bean) { + if (this.foreignEntity == null) return super.createPredicate(cache, bean); final Map foreign = new HashMap<>(); Predicate result = null; - putForeignPredicate(foreign, bean); + putForeignPredicate(cache, foreign, bean); if (this.nodes != null) { for (FilterNode n : this.nodes) { FilterBeanNode node = (FilterBeanNode) n; if (node.foreignEntity == null) { - Predicate f = node.createFilterPredicate(info, bean); + Predicate f = node.createPredicate(cache, bean); if (f == null) continue; final Predicate one = result; final Predicate two = f; - result = (result == null) ? f : (signand ? new Predicate() { + result = (result == null) ? f : (and ? new Predicate() { @Override public boolean test(T t) { @@ -276,7 +280,7 @@ final class FilterBeanNode extends FilterNode { } }); } else { - putForeignPredicate(foreign, bean); + putForeignPredicate(cache, foreign, bean); } } } @@ -284,8 +288,7 @@ final class FilterBeanNode extends FilterNode { final String byjoinCol = this.byjoinColumn; final Attribute foreignAttr = this.foreignAttribute; for (final Map.Entry en : foreign.entrySet()) { - Attribute byjoinAttr = info.getAttribute(byjoinCol); - final EntityCache cache = en.getKey().getCache(); + Attribute byjoinAttr = cache.getAttribute(byjoinCol); final Predicate p = en.getValue(); Predicate f = new Predicate() { @@ -303,7 +306,7 @@ final class FilterBeanNode extends FilterNode { }; final Predicate one = result; final Predicate two = f; - result = (result == null) ? f : (signand ? new Predicate() { + result = (result == null) ? f : (and ? new Predicate() { @Override public boolean test(T t) { @@ -330,21 +333,21 @@ final class FilterBeanNode extends FilterNode { return result; } - private void putForeignPredicate(final Map foreign, FilterBean bean) { + private void putForeignPredicate(final EntityCache cache, final Map foreign, FilterBean bean) { if (this.foreignEntity == null) return; - final Serializable val = getValue(bean); + final Serializable val = getElementValue(bean); Predicate filter = (val == null && express != ISNULL && express != ISNOTNULL) ? new Predicate() { @Override public boolean test(T t) { - return signand; + return and; } @Override public String toString() { - return "" + signand; + return "" + and; } - } : super.createFilterPredicate(this.columnAttribute, val); + } : super.createElementPredicate(cache, this.columnAttribute, bean); if (filter == null) return; Predicate p = foreign.get(this.foreignEntity); if (p == null) { @@ -352,7 +355,7 @@ final class FilterBeanNode extends FilterNode { } else { final Predicate one = p; final Predicate two = filter; - p = signand ? new Predicate() { + p = and ? new Predicate() { @Override public boolean test(T t) { @@ -380,12 +383,12 @@ final class FilterBeanNode extends FilterNode { } @Override - protected boolean isJoinAllCached() { + protected boolean isCacheUseable() { return joinallcached; } @Override - protected Serializable getValue(FilterBean bean) { + protected Serializable getElementValue(FilterBean bean) { if (bean == null || beanAttribute == null) return null; Serializable rs = (Serializable) beanAttribute.get(bean); if (rs == null) return null; diff --git a/src/com/wentch/redkale/source/FilterJoinNode.java b/src/com/wentch/redkale/source/FilterJoinNode.java new file mode 100644 index 000000000..fc7f8b265 --- /dev/null +++ b/src/com/wentch/redkale/source/FilterJoinNode.java @@ -0,0 +1,66 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.wentch.redkale.source; + +import java.util.*; + +/** + * + * @author zhangjx + */ +public final class FilterJoinNode extends FilterNode { + + private Class joinClass; + + private String joinColumn; + + private String foreignColumn; + + public FilterJoinNode() { + } + + protected FilterJoinNode(Class joinClass, String joinColumn, String foreignColumn) { + this.joinClass = joinClass; + this.joinColumn = joinColumn; + this.foreignColumn = foreignColumn; + } + + public static FilterJoinNode create(Class joinClass, String joinColumn) { + return create(joinClass, joinColumn, joinColumn); + } + + public static FilterJoinNode create(Class joinClass, String joinColumn, String foreignColumn) { + Objects.requireNonNull(joinClass); + Objects.requireNonNull(joinColumn); + Objects.requireNonNull(foreignColumn); + return new FilterJoinNode(joinClass, joinColumn, foreignColumn); + } + + public Class getJoinClass() { + return joinClass; + } + + public void setJoinClass(Class joinClass) { + this.joinClass = joinClass; + } + + public String getJoinColumn() { + return joinColumn; + } + + public void setJoinColumn(String joinColumn) { + this.joinColumn = joinColumn; + } + + public String getForeignColumn() { + return foreignColumn; + } + + public void setForeignColumn(String foreignColumn) { + this.foreignColumn = foreignColumn; + } + +} diff --git a/src/com/wentch/redkale/source/FilterNode.java b/src/com/wentch/redkale/source/FilterNode.java index f519f7647..b357213c5 100644 --- a/src/com/wentch/redkale/source/FilterNode.java +++ b/src/com/wentch/redkale/source/FilterNode.java @@ -38,12 +38,11 @@ public class FilterNode { protected Serializable value; //---------------------------------------------- - - protected boolean signand = true; + protected boolean and = true; protected FilterNode[] nodes; - protected FilterNode() { + public FilterNode() { } protected FilterNode(String col, FilterExpress exp, Serializable val) { @@ -69,7 +68,7 @@ public class FilterNode { } public final FilterNode and(String column, Serializable value) { - return and(new FilterNode(column, null, value)); + return and(column, null, value); } public final FilterNode and(String column, FilterExpress express, Serializable value) { @@ -81,7 +80,7 @@ public class FilterNode { } public final FilterNode or(String column, Serializable value) { - return or(new FilterNode(column, null, value)); + return or(column, null, value); } public final FilterNode or(String column, FilterExpress express, Serializable value) { @@ -90,12 +89,18 @@ public class FilterNode { protected final FilterNode any(FilterNode node, boolean sign) { Objects.requireNonNull(node); - if (nodes == null) { - nodes = new FilterNode[]{node}; - this.signand = sign; + if (this.column == null) { + this.column = node.column; + this.express = node.express; + this.value = node.value; return this; } - if (signand == sign) { + if (nodes == null) { + nodes = new FilterNode[]{node}; + this.and = sign; + return this; + } + if (and == sign) { FilterNode[] newsiblings = new FilterNode[nodes.length + 1]; System.arraycopy(nodes, 0, newsiblings, 0, nodes.length); newsiblings[nodes.length] = node; @@ -106,23 +111,51 @@ public class FilterNode { return this; } + /** + * 该方法需要重载 + * + * @param node + * @param sign + */ protected void append(FilterNode node, boolean sign) { FilterNode newnode = new FilterNode(this.column, this.express, this.value); - newnode.signand = this.signand; + newnode.and = this.and; newnode.nodes = this.nodes; this.nodes = new FilterNode[]{newnode, node}; this.tabalis = null; this.column = null; this.express = null; - this.signand = sign; + this.and = sign; this.value = null; } - protected Serializable getValue(FilterBean bean) { + /** + * 该方法需要重载 + * + * @param bean + * @return + */ + protected Serializable getElementValue(final FilterBean bean) { return value; } - protected boolean isJoinAllCached() { + /** + * 该方法需要重载 + * + * @param + * @param info + * @return + */ + protected CharSequence createSQLJoin(final EntityInfo info) { + return null; + } + + /** + * 该方法需要重载 + * + * @return + */ + protected boolean isCacheUseable() { return true; } @@ -134,50 +167,42 @@ public class FilterNode { return new FilterNode(column, express, value); } - protected final StringBuilder createFilterSQLExpress(final EntityInfo info, FilterBean bean) { - return createFilterSQLExpress(true, info, bean); - } - - protected StringBuilder createFilterSQLExpress(final boolean first, final EntityInfo info, FilterBean bean) { - final Serializable val = getValue(bean); - if (val == null && (express == ISNULL || express == ISNOTNULL)) return new StringBuilder(0); - StringBuilder sb0 = createFilterSQLExpress(info, val); - if (this.nodes == null) { - if (sb0 == null) return new StringBuilder(0); - if (!first) return sb0; - return new StringBuilder(sb0.length() + 8).append(" WHERE ").append(sb0); - } + protected final CharSequence createSQLExpress(final EntityInfo info, final FilterBean bean) { + CharSequence sb0 = createElementSQLExpress(info, bean); + if (this.nodes == null) return sb0; final StringBuilder rs = new StringBuilder(); - rs.append(first ? " WHERE (" : " ("); + rs.append('('); boolean more = false; if (sb0 != null && sb0.length() > 2) { more = true; rs.append(sb0); } for (FilterNode node : this.nodes) { - StringBuilder f = node.createFilterSQLExpress(false, info, bean); + CharSequence f = node.createSQLExpress(info, bean); if (f == null || f.length() < 3) continue; - if (more) rs.append(signand ? " AND " : " OR "); + if (more) rs.append(and ? " AND " : " OR "); rs.append(f); more = true; } rs.append(')'); - if (rs.length() < (first ? 10 : 5)) return new StringBuilder(0); + if (rs.length() < 5) return null; return rs; } - private StringBuilder createFilterSQLExpress(final EntityInfo info, Serializable val0) { + protected final CharSequence createElementSQLExpress(final EntityInfo info, final FilterBean bean) { if (column == null) return null; - final StringBuilder val = formatValue(val0); + if (express == ISNULL || express == ISNOTNULL) { + StringBuilder sb = new StringBuilder(); + if (tabalis != null) sb.append(tabalis).append('.'); + sb.append(info.getSQLColumn(column)).append(' ').append(express.value()); + return sb; + } + final StringBuilder val = formatToString(express, getElementValue(bean)); if (val == null) return null; - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(32); if (tabalis != null) sb.append(tabalis).append('.'); sb.append(info.getSQLColumn(column)).append(' '); switch (express) { - case ISNULL: - case ISNOTNULL: - sb.append(express.value()); - break; case OPAND: case OPOR: sb.append(express.value()).append(' ').append(val).append(" > 0"); @@ -192,45 +217,16 @@ public class FilterNode { return sb; } - protected static String createFilterSQLOrderBy(EntityInfo info, Flipper flipper) { - if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty()) return ""; - final String sort = flipper.getSort(); - String sql = info.getSortOrderbySql(sort); - if (sql != null) return sql; - final StringBuilder sb = new StringBuilder(); - sb.append(" ORDER BY "); - if (info.isNoAlias()) { - sb.append(sort); - } else { - boolean flag = false; - for (String item : sort.split(",")) { - if (item.isEmpty()) continue; - String[] sub = item.split("\\s+"); - if (flag) sb.append(','); - if (sub.length < 2 || sub[1].equalsIgnoreCase("ASC")) { - sb.append("a.").append(info.getSQLColumn(sub[0])).append(" ASC"); - } else { - sb.append("a.").append(info.getSQLColumn(sub[0])).append(" DESC"); - } - flag = true; - } - } - sql = sb.toString(); - info.putSortOrderbySql(sort, sql); - return sql; - } - - protected Predicate createFilterPredicate(final EntityInfo info, FilterBean bean) { - if (info == null || (column == null && this.nodes == null)) return null; - final Serializable val = getValue(bean); - Predicate filter = createFilterPredicate(column == null ? null : info.getAttribute(column), val); + protected Predicate createPredicate(final EntityCache cache, final FilterBean bean) { + if (cache == null || (column == null && this.nodes == null)) return null; + Predicate filter = createElementPredicate(cache, bean); if (this.nodes == null) return filter; for (FilterNode node : this.nodes) { - Predicate f = node.createFilterPredicate(info, bean); + Predicate f = node.createPredicate(cache, bean); if (f == null) continue; final Predicate one = filter; final Predicate two = f; - filter = (filter == null) ? f : (signand ? new Predicate() { + filter = (filter == null) ? f : (and ? new Predicate() { @Override public boolean test(T t) { @@ -257,35 +253,39 @@ public class FilterNode { return filter; } - protected final Predicate createFilterPredicate(final Attribute attr, Serializable val0) { - if (val0 == null) { - if (express == ISNULL) return new Predicate() { + protected final Predicate createElementPredicate(final EntityCache cache, final FilterBean bean) { + return createElementPredicate(cache, cache.getAttribute(column), bean); + } - @Override - public boolean test(T t) { - return attr.get(t) == null; - } + protected final Predicate createElementPredicate(final EntityCache cache, final Attribute attr, final FilterBean bean) { - @Override - public String toString() { - return attr.field() + " = null"; - } - }; - if (express == ISNOTNULL) return new Predicate() { + if (express == ISNULL) return new Predicate() { - @Override - public boolean test(T t) { - return attr.get(t) != null; - } + @Override + public boolean test(T t) { + return attr.get(t) == null; + } - @Override - public String toString() { - return attr.field() + " != null"; - } - }; - return null; - } + @Override + public String toString() { + return attr.field() + " = null"; + } + }; + if (express == ISNOTNULL) return new Predicate() { + + @Override + public boolean test(T t) { + return attr.get(t) != null; + } + + @Override + public String toString() { + return attr.field() + " != null"; + } + }; if (attr == null) return null; + Serializable val0 = getElementValue(bean); + if (val0 == null) return null; final Class atype = attr.type(); final Class valtype = val0.getClass(); @@ -743,174 +743,51 @@ public class FilterNode { return null; } - protected static Comparator createFilterComparator(EntityCache cache, Flipper flipper) { - if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty()) return null; - final String sort = flipper.getSort(); - Comparator comparator = cache.getSortComparator(sort); - if (comparator != null) return comparator; - for (String item : sort.split(",")) { - if (item.trim().isEmpty()) continue; - String[] sub = item.trim().split("\\s+"); - int pos = sub[0].indexOf('('); - Attribute attr; - if (pos <= 0) { - attr = cache.info.getAttribute(sub[0]); - } else { //含SQL函数 - int pos2 = sub[0].lastIndexOf(')'); - final Attribute pattr = cache.info.getAttribute(sub[0].substring(pos + 1, pos2)); - final String func = sub[0].substring(0, pos); - if ("ABS".equalsIgnoreCase(func)) { - if (pattr.type() == int.class || pattr.type() == Integer.class) { - attr = new Attribute() { - - @Override - public Class type() { - return pattr.type(); - } - - @Override - public Class declaringClass() { - return pattr.declaringClass(); - } - - @Override - public String field() { - return pattr.field(); - } - - @Override - public Serializable get(E obj) { - return Math.abs(((Number) pattr.get(obj)).intValue()); - } - - @Override - public void set(E obj, Serializable value) { - pattr.set(obj, value); - } - }; - } else if (pattr.type() == long.class || pattr.type() == Long.class) { - attr = new Attribute() { - - @Override - public Class type() { - return pattr.type(); - } - - @Override - public Class declaringClass() { - return pattr.declaringClass(); - } - - @Override - public String field() { - return pattr.field(); - } - - @Override - public Serializable get(E obj) { - return Math.abs(((Number) pattr.get(obj)).longValue()); - } - - @Override - public void set(E obj, Serializable value) { - pattr.set(obj, value); - } - }; - } else if (pattr.type() == float.class || pattr.type() == Float.class) { - attr = new Attribute() { - - @Override - public Class type() { - return pattr.type(); - } - - @Override - public Class declaringClass() { - return pattr.declaringClass(); - } - - @Override - public String field() { - return pattr.field(); - } - - @Override - public Serializable get(E obj) { - return Math.abs(((Number) pattr.get(obj)).floatValue()); - } - - @Override - public void set(E obj, Serializable value) { - pattr.set(obj, value); - } - }; - } else if (pattr.type() == double.class || pattr.type() == Double.class) { - attr = new Attribute() { - - @Override - public Class type() { - return pattr.type(); - } - - @Override - public Class declaringClass() { - return pattr.declaringClass(); - } - - @Override - public String field() { - return pattr.field(); - } - - @Override - public Serializable get(E obj) { - return Math.abs(((Number) pattr.get(obj)).doubleValue()); - } - - @Override - public void set(E obj, Serializable value) { - pattr.set(obj, value); - } - }; - } else { - throw new RuntimeException("Flipper not supported sort illegal type by ABS (" + flipper.getSort() + ")"); - } - } else if (func.isEmpty()) { - attr = pattr; - } else { - throw new RuntimeException("Flipper not supported sort illegal function (" + flipper.getSort() + ")"); - } - } - Comparator c = (sub.length > 1 && sub[1].equalsIgnoreCase("DESC")) ? (E o1, E o2) -> { - Comparable c1 = (Comparable) attr.get(o1); - Comparable c2 = (Comparable) attr.get(o2); - return c2 == null ? -1 : c2.compareTo(c1); - } : (E o1, E o2) -> { - Comparable c1 = (Comparable) attr.get(o1); - Comparable c2 = (Comparable) attr.get(o2); - return c1 == null ? -1 : c1.compareTo(c2); - }; - - if (comparator == null) { - comparator = c; - } else { - comparator = comparator.thenComparing(c); - } - } - cache.putSortComparator(sort, comparator); - return comparator; + @Override + public String toString() { + return toString(null); } - protected StringBuilder formatValue(Object value) { - return formatValue(express, value); + public String toString(final FilterBean bean) { + StringBuilder sb = new StringBuilder(); + if (nodes == null) { + if (column != null) { + Serializable ev = getElementValue(bean); + if (express == ISNULL || express == ISNOTNULL) { + sb.append(column).append(' ').append(express.value()); + } else if (ev != null) { + sb.append(column).append(' ').append(express.value()).append(' ').append(formatToString(express, ev)); + } + } + } else { + boolean more = false; + if (column != null) { + Serializable ev = getElementValue(bean); + if (express == ISNULL || express == ISNOTNULL) { + sb.append('(').append(column).append(' ').append(express.value()); + more = true; + } else if (ev != null) { + sb.append('(').append(column).append(' ').append(express.value()).append(' ').append(formatToString(express, ev)); + more = true; + } + } + for (FilterNode node : this.nodes) { + String s = node.toString(); + if (s.isEmpty()) continue; + if (sb.length() > 0) sb.append(and ? " AND " : " OR "); + sb.append(s); + } + if (more) sb.append(')'); + } + return sb.toString(); } protected static String formatToString(Object value) { - StringBuilder sb = formatValue(null, value); + StringBuilder sb = formatToString(null, value); return sb == null ? null : sb.toString(); } - private static StringBuilder formatValue(FilterExpress express, Object value) { + private static StringBuilder formatToString(FilterExpress express, Object value) { if (value == null) return null; if (value instanceof Number) return new StringBuilder().append(value); if (value instanceof CharSequence) { @@ -937,7 +814,7 @@ public class FilterNode { if (len == 0) return express == NOTIN ? null : new StringBuilder("(NULL)"); if (len == 1) { Object firstval = Array.get(value, 0); - if (firstval != null && firstval.getClass().isArray()) return formatValue(express, firstval); + if (firstval != null && firstval.getClass().isArray()) return formatToString(express, firstval); } StringBuilder sb = new StringBuilder(); sb.append('('); @@ -969,67 +846,51 @@ public class FilterNode { return new StringBuilder().append(value); } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (nodes == null) { - sb.append(column).append(' ').append(express.value()).append(' ').append(formatValue(value)); - } else { - if (column != null) sb.append('(').append(column).append(' ').append(express.value()).append(' ').append(formatValue(value)); - for (FilterNode node : this.nodes) { - if (sb.length() > 0) sb.append(signand ? " AND " : " OR "); - sb.append(node.toString()); - } - sb.append(')'); - } - return sb.toString(); - } - - public Serializable getValue() { + public final Serializable getValue() { return value; } - public void setValue(Serializable value) { + public final void setValue(Serializable value) { this.value = value; } - public boolean isSignand() { - return signand; + public final boolean isAnd() { + return and; } - public void setSignand(boolean signand) { - this.signand = signand; + public final void setAnd(boolean and) { + this.and = and; } - public String getTabalis() { + public final String getTabalis() { return tabalis; } - public void setTabalis(String tabalis) { + public final void setTabalis(String tabalis) { this.tabalis = tabalis; } - public String getColumn() { + public final String getColumn() { return column; } - public void setColumn(String column) { + public final void setColumn(String column) { this.column = column; } - public FilterExpress getExpress() { + public final FilterExpress getExpress() { return express; } - public void setExpress(FilterExpress express) { + public final void setExpress(FilterExpress express) { this.express = express; } - public FilterNode[] getNodes() { + public final FilterNode[] getNodes() { return nodes; } - public void setNodes(FilterNode[] nodes) { + public final void setNodes(FilterNode[] nodes) { this.nodes = nodes; }