org.redkale.source.FilterJoinNode Maven / Gradle / Ivy
/*
* 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 org.redkale.source;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.*;
import static org.redkale.source.FilterExpress.EQUAL;
import org.redkale.util.*;
/**
* @FilterJoinColumn对应的FilterNode对象
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class FilterJoinNode extends FilterNode {
private Class joinClass;
private EntityInfo joinEntity; //在调用 createSQLJoin 和 isCacheUseable 时会注入
private String[] joinColumns;
public FilterJoinNode() {
}
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 == null ? EQUAL : express;
this.itemand = itemand;
this.value = value;
}
protected FilterJoinNode(FilterJoinNode node) {
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 create(joinClass, new String[]{joinColumn}, column, value);
}
public static FilterJoinNode create(Class joinClass, String joinColumn, String column, FilterExpress express, Serializable 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 create(joinClass, joinColumns, column, null, value);
}
public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, FilterExpress express, Serializable 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
public FilterJoinNode copy() {
FilterJoinNode node = (FilterJoinNode) copy(new FilterJoinNode());
node.joinClass = this.joinClass;
node.joinEntity = this.joinEntity;
if (this.joinColumns != null) {
node.joinColumns = new String[this.joinColumns.length];
System.arraycopy(this.joinColumns, 0, node.joinColumns, 0, this.joinColumns.length);
}
return node;
}
@Override
protected FilterNode any(final FilterNode node0, boolean signor) {
Objects.requireNonNull(node0);
if (!(node0 instanceof FilterJoinNode)) {
throw new IllegalArgumentException(this + (signor ? " or " : " and ") + " a node but " + String.valueOf(node0) + " is not a " + FilterJoinNode.class.getSimpleName());
}
final FilterJoinNode node = (FilterJoinNode) node0;
if (this.nodes == null) {
this.nodes = new FilterNode[]{node};
this.or = signor;
return this;
}
if (or == signor || this.column == null) {
this.nodes = Utility.append(this.nodes, node);
if (this.column == null) this.or = signor;
return this;
}
this.nodes = new FilterNode[]{new FilterJoinNode(this), node};
this.column = null;
this.express = null;
this.itemand = true;
this.value = null;
this.joinClass = null;
this.joinEntity = null;
this.joinColumns = null;
this.or = signor;
return this;
}
@Override
protected CharSequence createSQLExpress(final EntityInfo info, final Map joinTabalis) {
return super.createSQLExpress(this.joinEntity == null ? info : this.joinEntity, joinTabalis);
}
@Override
protected Predicate createPredicate(final EntityCache cache) {
if (column == null && this.nodes == null) return null;
final EntityCache joinCache = this.joinEntity.getCache();
final AtomicBoolean more = new AtomicBoolean();
Predicate filter = createJoinPredicate(more);
Predicate rs = null;
if (filter == null && !more.get()) return rs;
if (filter != null) {
final Predicate inner = filter;
final String[][] localJoinColumns = new String[joinColumns.length][2];
for (int i = 0; i < joinColumns.length; i++) {
int pos = joinColumns[i].indexOf('=');
if (pos > 0) {
localJoinColumns[i] = new String[]{joinColumns[i].substring(0, pos), joinColumns[i].substring(pos + 1)};
} else {
localJoinColumns[i] = new String[]{joinColumns[i], joinColumns[i]};
}
}
rs = new Predicate() {
@Override
public boolean test(final T t) {
Predicate joinPredicate = null;
for (String[] localJoinColumn : localJoinColumns) {
final Serializable key = cache.getAttribute(localJoinColumn[0]).get(t);
final Attribute joinAttr = joinCache.getAttribute(localJoinColumn[1]);
Predicate p = (E e) -> key.equals(joinAttr.get(e));
joinPredicate = joinPredicate == null ? p : joinPredicate.and(p);
}
return joinCache.exists(inner.and(joinPredicate));
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(" #-- ON ").append(localJoinColumns[0][0]).append("=").append(joinClass == null ? "null" : joinClass.getSimpleName()).append(".").append(localJoinColumns[0][1]);
for (int i = 1; i < localJoinColumns.length; i++) {
sb.append(" AND ").append(localJoinColumns[i][0]).append("=").append(joinClass == null ? "null" : joinClass.getSimpleName()).append(".").append(localJoinColumns[i][1]);
}
sb.append(" --# ").append(inner.toString());
return sb.toString();
}
};
}
if (more.get()) { //存在不同Class的关联表
if (this.nodes != null) {
for (FilterNode node : this.nodes) {
if (((FilterJoinNode) node).joinClass == this.joinClass) continue;
Predicate f = node.createPredicate(cache);
if (f == null) continue;
final Predicate one = rs;
final Predicate two = f;
rs = (rs == null) ? f : (or ? 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 rs;
}
private Predicate createJoinPredicate(final AtomicBoolean more) {
if (column == null && this.nodes == null) return null;
final EntityCache joinCache = this.joinEntity.getCache();
Predicate filter = createElementPredicate(joinCache, true);
if (this.nodes != null) {
for (FilterNode node : this.nodes) {
if (((FilterJoinNode) node).joinClass != this.joinClass) {
more.set(true);
continue;
}
Predicate f = ((FilterJoinNode) node).createJoinPredicate(more);
if (f == null) continue;
final Predicate one = filter;
final Predicate two = f;
filter = (filter == null) ? f : (or ? new Predicate() {
@Override
public boolean test(E t) {
return one.test(t) || two.test(t);
}
@Override
public String toString() {
return "(" + one + " OR " + two + ")";
}
} : new Predicate() {
@Override
public boolean test(E t) {
return one.test(t) && two.test(t);
}
@Override
public String toString() {
return "(" + one + " AND " + two + ")";
}
});
}
}
return filter;
}
@Override
protected CharSequence createSQLJoin(final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) {
boolean morejoin = false;
if (this.joinEntity == null) {
if (this.joinClass != null) this.joinEntity = func.apply(this.joinClass);
if (this.nodes != null) {
for (FilterNode node : this.nodes) {
if (node instanceof FilterJoinNode) {
FilterJoinNode joinNode = ((FilterJoinNode) node);
if (joinNode.joinClass != null) {
joinNode.joinEntity = func.apply(joinNode.joinClass);
if (this.joinClass != null && this.joinClass != joinNode.joinClass) morejoin = true;
}
}
}
}
}
StringBuilder sb = new StringBuilder();
if (this.joinClass != null) {
CharSequence cs = createElementSQLJoin(update, joinTabalis, haset, info, this);
if (cs != null) sb.append(cs);
}
if (morejoin) {
Set set = new HashSet<>();
if (this.joinClass != null) set.add(this.joinClass);
for (FilterNode node : this.nodes) {
if (node instanceof FilterJoinNode) {
FilterJoinNode joinNode = ((FilterJoinNode) node);
if (!set.contains(joinNode.joinClass)) {
CharSequence cs = createElementSQLJoin(update, joinTabalis, haset, info, joinNode);
if (cs != null) {
sb.append(cs);
set.add(joinNode.joinClass);
}
}
}
}
}
return sb;
}
private static CharSequence createElementSQLJoin(final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info, final FilterJoinNode node) {
if (node.joinClass == null || (haset != null && haset.contains(joinTabalis.get(node.joinClass)))) return null;
StringBuilder sb = new StringBuilder();
String[] joinColumns = node.joinColumns;
int pos = joinColumns[0].indexOf('=');
if (update) {
sb.append("[").append(node.joinEntity.getTable(node)).append(" ").append(joinTabalis.get(node.joinClass)).append(']');
sb.append('{').append(info.getSQLColumn("a", pos > 0 ? joinColumns[0].substring(0, pos) : joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[0].substring(pos + 1) : joinColumns[0]));
for (int i = 1; i < joinColumns.length; i++) {
pos = joinColumns[i].indexOf('=');
sb.append(" AND ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[i].substring(0, pos) : joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[i].substring(pos + 1) : joinColumns[i]));
}
sb.append('}');
} else {
sb.append(" INNER JOIN ").append(node.joinEntity.getTable(node)).append(" ").append(joinTabalis.get(node.joinClass))
.append(" ON ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[0].substring(0, pos) : joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[0].substring(pos + 1) : joinColumns[0]));
for (int i = 1; i < joinColumns.length; i++) {
pos = joinColumns[i].indexOf('=');
sb.append(" AND ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[i].substring(0, pos) : joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[i].substring(pos + 1) : joinColumns[i]));
}
}
if (haset != null) haset.add(joinTabalis.get(node.joinClass));
return sb;
}
@Override
protected boolean isCacheUseable(final Function entityApplyer) {
if (this.joinEntity == null) this.joinEntity = entityApplyer.apply(this.joinClass);
if (!this.joinEntity.isCacheFullLoaded()) return false;
if (this.nodes == null) return true;
for (FilterNode node : this.nodes) {
if (!node.isCacheUseable(entityApplyer)) return false;
}
return true;
}
@Override
protected void putJoinTabalis(Map map) {
if (this.joinClass != null && !map.containsKey(this.joinClass)) map.put(joinClass, String.valueOf((char) ('b' + map.size())));
if (this.nodes == null) return;
for (FilterNode node : this.nodes) {
node.putJoinTabalis(map);
}
}
@Override
protected final boolean isjoin() {
return true;
}
@Override
public String toString() {
return toString(joinClass == null ? null : joinClass.getSimpleName()).toString();
}
public Class getJoinClass() {
return joinClass;
}
public void setJoinClass(Class joinClass) {
this.joinClass = joinClass;
}
public String[] getJoinColumns() {
return joinColumns;
}
public void setJoinColumns(String[] joinColumns) {
this.joinColumns = joinColumns;
}
}