diff --git a/src/com/wentch/redkale/source/DataJDBCSource.java b/src/com/wentch/redkale/source/DataJDBCSource.java index d6734bc2b..6bcab6858 100644 --- a/src/com/wentch/redkale/source/DataJDBCSource.java +++ b/src/com/wentch/redkale/source/DataJDBCSource.java @@ -1749,7 +1749,7 @@ public final class DataJDBCSource implements DataSource { if (valid) filter = finfo.getFilterPredicate(info, bean); } if (valid) { - Sheet sheet = cache.querySheet(selects, filter, flipper, FilterNode.getSortComparator(info, flipper)); + Sheet sheet = cache.querySheet(selects, filter, flipper, FilterNode.createFilterComparator(info, flipper)); if (!sheet.isEmpty() || cache.isFullLoaded()) return sheet; } } diff --git a/src/com/wentch/redkale/source/EntityInfo.java b/src/com/wentch/redkale/source/EntityInfo.java index 6a2410e0d..c25b3e095 100644 --- a/src/com/wentch/redkale/source/EntityInfo.java +++ b/src/com/wentch/redkale/source/EntityInfo.java @@ -209,7 +209,7 @@ public final class EntityInfo { this.allocationSize = allocationSize0; //----------------cache-------------- Cacheable c = type.getAnnotation(Cacheable.class); - boolean cf = (c == null) ? (cacheClasses != null && cacheClasses.contains(type)) : false; + boolean cf = (c == null && cacheClasses != null && cacheClasses.contains(type)); if ((c != null && c.value()) || cf) { this.cache = new EntityCache<>(type, creator, primary, attributes, fullloader); } else { diff --git a/src/com/wentch/redkale/source/FilterBeanNode.java b/src/com/wentch/redkale/source/FilterBeanNode.java index 96569c442..3b249d9ec 100644 --- a/src/com/wentch/redkale/source/FilterBeanNode.java +++ b/src/com/wentch/redkale/source/FilterBeanNode.java @@ -6,19 +6,141 @@ package com.wentch.redkale.source; import static com.wentch.redkale.source.FilterExpress.*; -import com.wentch.redkale.util.Attribute; +import com.wentch.redkale.util.*; import java.io.Serializable; import java.lang.reflect.*; -import java.util.Collection; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.logging.Logger; +import javax.persistence.Transient; /** * * @author zhangjx */ -public class FilterBeanNode extends FilterNode { +@SuppressWarnings("unchecked") +final class FilterBeanNode extends FilterNode { + private static final Logger logger = Logger.getLogger(FilterBeanNode.class.getSimpleName()); + + private static final ConcurrentHashMap beanodes = new ConcurrentHashMap<>(); + + public static FilterBeanNode load(Class clazz, final int nodeid, + Function fullloader) { + FilterBeanNode rs = beanodes.get(clazz); + if (rs != null) return rs; + synchronized (beanodes) { + rs = beanodes.get(clazz); + if (rs == null) { + rs = createNode(clazz, nodeid, fullloader); + beanodes.put(clazz, rs); + } + return rs; + } + } + + private static FilterBeanNode createNode(Class clazz, final int nodeid, + Function fullloader) { + Class cltmp = clazz; + Set fields = new HashSet<>(); + final Map joinTables = new HashMap<>(); + Map nodemap = new HashMap<>(); + boolean joinallcached = true; + final StringBuilder joinsb = new StringBuilder(); + do { + for (Field field : cltmp.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (fields.contains(field.getName())) continue; + if (field.getAnnotation(Ignore.class) != null) continue; + if (field.getAnnotation(Transient.class) != null) continue; + + char[] chars = field.getName().toCharArray(); + chars[0] = Character.toUpperCase(chars[0]); + final Class t = field.getType(); + Method getter = null; + try { + getter = cltmp.getMethod(((t == boolean.class || t == Boolean.class) ? "is" : "get") + new String(chars)); + } catch (Exception ex) { + continue; + } + fields.add(field.getName()); + FilterBeanNode newnode = new FilterBeanNode(field.getName(), true, Attribute.create(getter, null)); + newnode.setField(field); + //------------------------------------ + { + FilterJoinColumn joinCol = field.getAnnotation(FilterJoinColumn.class); + if (joinCol != null) { + boolean first = false; + final Class joinClass = joinCol.table(); + if (!joinTables.containsKey(joinClass)) { + first = true; + joinTables.put(joinClass, String.valueOf((char) ('b' + joinTables.size()))); + } + final String alias = joinTables.get(joinClass); + final EntityInfo secinfo = EntityInfo.load(joinClass, nodeid, fullloader); + if (secinfo.getCache() == null || !secinfo.getCache().isFullLoaded()) { + joinallcached = false; + } + if (first) { + joinsb.append(" ").append(joinCol.type().name()).append(" JOIN ").append(secinfo.getTable()) + .append(" ").append(alias).append(" ON a.# = ").append(alias).append(".") + .append(joinCol.column().isEmpty() ? secinfo.getPrimarySQLColumn() : secinfo.getSQLColumn(joinCol.column())); + } + newnode.foreignEntity = secinfo; + newnode.tabalis = alias; + newnode.foreignColumn = joinCol.column().isEmpty() ? secinfo.getPrimary().field() : joinCol.column(); + } + } + //------------------------------------ + { + FilterGroup[] refs = field.getAnnotationsByType(FilterGroup.class); + String[] groups = new String[refs.length]; + for (int i = 0; i < refs.length; i++) { + groups[i] = refs[i].value(); + } + if (groups.length == 0) groups = new String[]{"[AND]"}; + for (String key : groups) { + if (!key.startsWith("[AND]") && !key.startsWith("[OR]")) { + throw new RuntimeException(field + "'s FilterGroup.value(" + key + ") illegal, must be [AND] or [OR] startsWith"); + } + FilterBeanNode node = nodemap.get(key); + if (node == null) { + nodemap.put(key, newnode); + } else { + node.any(node, !key.contains("[OR]")); + } + } + } + } + } while ((cltmp = cltmp.getSuperclass()) != Object.class); + FilterBeanNode rs = null; + for (FilterBeanNode f : nodemap.values()) { + if (rs == null) { + rs = f; + } else { + rs.and(f); + } + } + if (rs != null) { + rs.joinallcached = joinallcached; + if (joinsb.length() > 0) rs.joinSQL = joinsb.toString(); + } + return rs; + } + + //--------------------------- only header ----------------------------------------------------- + private boolean joinallcached = true; + + private String joinSQL; + + //--------------------------------------------------------------------------------------------- private Attribute beanAttribute; + private EntityInfo foreignEntity; + + private String foreignColumn; + private boolean array; private boolean collection; @@ -39,7 +161,7 @@ public class FilterBeanNode extends FilterNode { this.beanAttribute = beanAttr; } - void setField(Field field) { + private void setField(Field field) { final FilterColumn fc = field.getAnnotation(FilterColumn.class); if (fc != null && !fc.name().isEmpty()) this.column = fc.name(); final Class type = field.getType(); @@ -63,6 +185,7 @@ public class FilterBeanNode extends FilterNode { } if (exp == null) exp = EQUAL; this.express = exp; + this.tabalis = "a"; } @Override @@ -70,6 +193,8 @@ public class FilterBeanNode extends FilterNode { FilterBeanNode newnode = new FilterBeanNode(this.column, this.signand, this.beanAttribute); newnode.express = this.express; newnode.nodes = this.nodes; + newnode.foreignEntity = this.foreignEntity; + newnode.foreignColumn = this.foreignColumn; newnode.array = this.array; newnode.collection = this.collection; newnode.ignoreCase = this.ignoreCase; @@ -85,6 +210,8 @@ public class FilterBeanNode extends FilterNode { if (node instanceof FilterBeanNode) { FilterBeanNode beanNode = ((FilterBeanNode) node); this.beanAttribute = beanNode.beanAttribute; + this.foreignEntity = beanNode.foreignEntity; + this.foreignColumn = beanNode.foreignColumn; this.array = beanNode.array; this.collection = beanNode.collection; this.ignoreCase = beanNode.ignoreCase; @@ -95,6 +222,19 @@ public class FilterBeanNode extends FilterNode { } } + @Override + protected StringBuilder createFilterSQLExpress(final EntityInfo info, FilterBean bean) { + if (joinSQL == null) return super.createFilterSQLExpress(info, bean); + StringBuilder sb = super.createFilterSQLExpress(info, bean); + String jsql = joinSQL.replace("#", info.getPrimarySQLColumn()); + return new StringBuilder(sb.length() + jsql.length()).append(jsql).append(sb); + } + + @Override + protected boolean isJoinAllCached() { + return false && joinallcached; //暂时没实现 + } + @Override protected Serializable getValue(FilterBean bean) { if (bean == null || beanAttribute == null) return null; diff --git a/src/com/wentch/redkale/source/FilterNode.java b/src/com/wentch/redkale/source/FilterNode.java index f024159f0..10071dfbf 100644 --- a/src/com/wentch/redkale/source/FilterNode.java +++ b/src/com/wentch/redkale/source/FilterNode.java @@ -20,6 +20,8 @@ public class FilterNode { protected boolean signand = true; + protected String tabalis; + protected String column; protected FilterExpress express; @@ -96,6 +98,7 @@ public class FilterNode { newnode.signand = this.signand; newnode.nodes = this.nodes; this.nodes = new FilterNode[]{newnode}; + this.tabalis = node.tabalis; this.column = node.column; this.express = node.express; this.signand = sign; @@ -106,6 +109,10 @@ public class FilterNode { return value; } + protected boolean isJoinAllCached() { + return true; + } + public static FilterNode create(String column, Serializable value) { return create(column, FilterExpress.EQUAL, value); } @@ -114,13 +121,16 @@ public class FilterNode { return new FilterNode(column, express, value); } - protected final StringBuilder createFilterSQLExpress(final EntityInfo info, FilterBean bean) { + protected StringBuilder createFilterSQLExpress(final EntityInfo info, FilterBean bean) { final Serializable val = getValue(bean); - if (val == null && express != ISNULL && express != ISNOTNULL) return null; + if (val == null && express != ISNULL && express != ISNOTNULL) return new StringBuilder(0); StringBuilder sb0 = createFilterSQLExpress(info, val); - if (nodes == null) return sb0; + if (nodes == null) { + if (sb0 == null) return new StringBuilder(0); + return new StringBuilder(sb0.length() + 8).append(" WHERE ").append(sb0); + } final StringBuilder rs = new StringBuilder(); - rs.append('('); + rs.append(" WHERE ("); if (sb0 != null) rs.append(sb0); for (FilterNode node : this.nodes) { StringBuilder f = node.createFilterSQLExpress(info, bean); @@ -129,7 +139,7 @@ public class FilterNode { rs.append(f); } rs.append(')'); - if (rs.length() < 3) return null; + if (rs.length() < 10) return new StringBuilder(0); return rs; } @@ -137,6 +147,7 @@ public class FilterNode { final StringBuilder val = formatValue(val0); if (val == null) return null; StringBuilder sb = new StringBuilder(); + if (tabalis != null) sb.append(tabalis).append('.'); sb.append(info.getSQLColumn(column)).append(' '); switch (express) { case ISNULL: @@ -261,7 +272,30 @@ public class FilterNode { return null; } - protected static Comparator getSortComparator(EntityInfo info, Flipper flipper) { + protected static StringBuilder createFilterSQLOrderBy(EntityInfo info, Flipper flipper) { + if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty()) return null; + final StringBuilder sb = new StringBuilder(); + sb.append(" ORDER BY "); + if (info.isNoAlias()) { + sb.append(flipper.getSort()); + } else { + boolean flag = false; + for (String item : flipper.getSort().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; + } + } + return sb; + } + + protected static Comparator createFilterComparator(EntityInfo info, Flipper flipper) { if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty()) return null; Comparator comparator = null; for (String item : flipper.getSort().split(",")) { @@ -371,6 +405,14 @@ public class FilterNode { this.signand = signand; } + public String getTabalis() { + return tabalis; + } + + public void setTabalis(String tabalis) { + this.tabalis = tabalis; + } + public String getColumn() { return column; }