org.redkale.source.EntityCache 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.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.logging.*;
import java.util.stream.*;
import javax.persistence.*;
import static org.redkale.source.FilterFunc.*;
import org.redkale.util.*;
/**
* Entity数据的缓存类
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
* @param Entity类的泛型
*/
@SuppressWarnings("unchecked")
public final class EntityCache {
//日志
private static final Logger logger = Logger.getLogger(EntityCache.class.getName());
//主键与对象的键值对
private ConcurrentHashMap map = new ConcurrentHashMap();
// CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%;
private Collection list = new ConcurrentLinkedQueue();
//Flipper.sort转换成Comparator的缓存
private final Map> sortComparators = new ConcurrentHashMap<>();
//Entity类
private final Class type;
//接口返回的对象是否需要复制一份
private final boolean needcopy;
//Entity构建器
private final Creator creator;
//主键字段
private final Attribute primary;
//新增时的复制器, 排除了标记为@Transient的字段
private final Reproduce newReproduce;
//修改时的复制器, 排除了标记为@Transient或@Column(updatable=false)的字段
private final Reproduce chgReproduce;
//是否已经全量加载过
private volatile boolean fullloaded;
//Entity信息
final EntityInfo info;
//@Cacheable的定时更新秒数,为0表示不定时更新
final int interval;
//@Cacheable的定时器
private ScheduledThreadPoolExecutor scheduler;
public EntityCache(final EntityInfo info, final Cacheable c) {
this.info = info;
this.interval = c == null ? 0 : c.interval();
this.type = info.getType();
this.creator = info.getCreator();
this.primary = info.primary;
VirtualEntity ve = info.getType().getAnnotation(VirtualEntity.class);
this.needcopy = ve == null || !ve.direct();
this.newReproduce = Reproduce.create(type, type, (m) -> {
try {
return type.getDeclaredField(m).getAnnotation(Transient.class) == null;
} catch (Exception e) {
return true;
}
});
this.chgReproduce = Reproduce.create(type, type, (m) -> {
try {
java.lang.reflect.Field field = type.getDeclaredField(m);
if (field.getAnnotation(Transient.class) != null) return false;
Column column = field.getAnnotation(Column.class);
return (column == null || column.updatable());
} catch (Exception e) {
return true;
}
});
}
public void fullLoad() {
if (info.fullloader == null) {
this.list = new ConcurrentLinkedQueue();
this.map = new ConcurrentHashMap();
this.fullloaded = true;
return;
}
this.fullloaded = false;
ConcurrentHashMap newmap = new ConcurrentHashMap();
List all = info.fullloader.apply(info.source, type);
if (all != null) {
all.stream().filter(x -> x != null).forEach(x -> {
newmap.put(this.primary.get(x), x);
});
}
this.list = all == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all);
this.map = newmap;
this.fullloaded = true;
if (this.interval > 0) {
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, "EntityCache-" + type + "-Thread");
t.setDaemon(true);
return t;
});
this.scheduler.scheduleAtFixedRate(() -> {
try {
ConcurrentHashMap newmap2 = new ConcurrentHashMap();
List all2 = info.fullloader.apply(info.source, type);
if (all2 != null) {
all2.stream().filter(x -> x != null).forEach(x -> {
newmap2.put(this.primary.get(x), x);
});
}
this.list = all2 == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all2);
this.map = newmap2;
} catch (Throwable t) {
logger.log(Level.SEVERE, type + " schedule(interval=" + interval + "s) Cacheable error", t);
}
}, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS);
}
}
public Class getType() {
return type;
}
public void clear() {
this.fullloaded = false;
this.list = new ConcurrentLinkedQueue();
this.map = new ConcurrentHashMap();
if (this.scheduler != null) {
this.scheduler.shutdownNow();
this.scheduler = null;
}
}
public boolean isFullLoaded() {
return fullloaded;
}
public T find(Serializable id) {
if (id == null) return null;
T rs = map.get(id);
return rs == null ? null : (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs);
}
public T find(final SelectColumn selects, final Serializable id) {
if (id == null) return null;
T rs = map.get(id);
if (rs == null) return null;
if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs);
T t = this.creator.create();
for (Attribute attr : this.info.attributes) {
if (selects.test(attr.field())) attr.set(t, attr.get(rs));
}
return t;
}
public T find(final SelectColumn selects, FilterNode node) {
final Predicate filter = node == null ? null : node.createPredicate(this);
Stream stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
Optional opt = stream.findFirst();
if (!opt.isPresent()) return null;
if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), opt.get()) : opt.get());
T rs = opt.get();
T t = this.creator.create();
for (Attribute attr : this.info.attributes) {
if (selects.test(attr.field())) attr.set(t, attr.get(rs));
}
return t;
}
public Serializable findColumn(final String column, final Serializable defValue, final Serializable id) {
if (id == null) return defValue;
T rs = map.get(id);
if (rs == null) return defValue;
for (Attribute attr : this.info.attributes) {
if (column.equals(attr.field())) {
Serializable val = (Serializable) attr.get(rs);
return val == null ? defValue : val;
}
}
return defValue;
}
public Serializable findColumn(final String column, final Serializable defValue, FilterNode node) {
final Predicate filter = node == null ? null : node.createPredicate(this);
Stream stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
Optional opt = stream.findFirst();
if (!opt.isPresent()) return defValue;
T rs = opt.get();
for (Attribute attr : this.info.attributes) {
if (column.equals(attr.field())) {
Serializable val = (Serializable) attr.get(rs);
return val == null ? defValue : val;
}
}
return defValue;
}
public boolean exists(Serializable id) {
if (id == null) return false;
final Class atype = this.primary.type();
if (id.getClass() != atype && id instanceof Number) {
if (atype == int.class || atype == Integer.class) {
id = ((Number) id).intValue();
} else if (atype == long.class || atype == Long.class) {
id = ((Number) id).longValue();
} else if (atype == short.class || atype == Short.class) {
id = ((Number) id).shortValue();
} else if (atype == float.class || atype == Float.class) {
id = ((Number) id).floatValue();
} else if (atype == byte.class || atype == Byte.class) {
id = ((Number) id).byteValue();
} else if (atype == double.class || atype == Double.class) {
id = ((Number) id).doubleValue();
} else if (atype == AtomicInteger.class) {
id = new AtomicInteger(((Number) id).intValue());
} else if (atype == AtomicLong.class) {
id = new AtomicLong(((Number) id).longValue());
}
}
return this.map.containsKey(id);
}
public boolean exists(FilterNode node) {
final Predicate filter = node == null ? null : node.createPredicate(this);
Stream stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
return stream.findFirst().isPresent();
}
public boolean exists(final Predicate filter) {
return (filter != null) && this.list.stream().filter(filter).findFirst().isPresent();
}
public Map queryColumnMap(final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) {
final Attribute keyAttr = info.getAttribute(keyColumn);
final Predicate filter = node == null ? null : node.createPredicate(this);
final Attribute funcAttr = funcColumn == null ? null : info.getAttribute(funcColumn);
Stream stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
Collector collector = null;
final Class valtype = funcAttr == null ? null : funcAttr.type();
switch (func) {
case AVG:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector) Collectors.averagingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector) Collectors.averagingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
case COUNT:
collector = (Collector) Collectors.counting();
break;
case DISTINCTCOUNT:
collector = (Collector) Collectors.mapping((t) -> funcAttr.get(t), Collectors.toSet());
break;
case MAX:
case MIN:
Comparator comp = (o1, o2) -> o1 == null ? (o2 == null ? 0 : -1) : ((Comparable) funcAttr.get(o1)).compareTo(funcAttr.get(o2));
collector = (Collector) ((func == MAX) ? Collectors.maxBy(comp) : Collectors.minBy(comp));
break;
case SUM:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector) Collectors.summingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector) Collectors.summingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
}
Map rs = stream.collect(Collectors.groupingBy(t -> keyAttr.get(t), LinkedHashMap::new, collector));
if (func == MAX || func == MIN) {
Map rs2 = new LinkedHashMap();
rs.forEach((x, y) -> {
if (((Optional) y).isPresent()) rs2.put(x, funcAttr.get((T) ((Optional) y).get()));
});
rs = rs2;
} else if (func == DISTINCTCOUNT) {
Map rs2 = new LinkedHashMap();
rs.forEach((x, y) -> rs2.put(x, ((Set) y).size() + 0L));
rs = rs2;
}
return rs;
}
public Number getNumberResult(final FilterFunc func, final Number defResult, final String column, final FilterNode node) {
final Attribute attr = column == null ? null : info.getAttribute(column);
final Predicate filter = node == null ? null : node.createPredicate(this);
Stream stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
switch (func) {
case AVG:
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
OptionalDouble rs = stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).average();
return rs.isPresent() ? (int) rs.getAsDouble() : defResult;
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
OptionalDouble rs = stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).average();
return rs.isPresent() ? (long) rs.getAsDouble() : defResult;
} else if (attr.type() == short.class || attr.type() == Short.class) {
OptionalDouble rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).average();
return rs.isPresent() ? (short) rs.getAsDouble() : defResult;
} else if (attr.type() == float.class || attr.type() == Float.class) {
OptionalDouble rs = stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).average();
return rs.isPresent() ? (float) rs.getAsDouble() : defResult;
} else if (attr.type() == double.class || attr.type() == Double.class) {
OptionalDouble rs = stream.mapToDouble(x -> (Double) attr.get(x)).average();
return rs.isPresent() ? rs.getAsDouble() : defResult;
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
case COUNT:
return stream.count();
case DISTINCTCOUNT:
return stream.map(x -> attr.get(x)).distinct().count();
case MAX:
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
OptionalInt rs = stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).max();
return rs.isPresent() ? rs.getAsInt() : defResult;
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
OptionalLong rs = stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).max();
return rs.isPresent() ? rs.getAsLong() : defResult;
} else if (attr.type() == short.class || attr.type() == Short.class) {
OptionalInt rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).max();
return rs.isPresent() ? (short) rs.getAsInt() : defResult;
} else if (attr.type() == float.class || attr.type() == Float.class) {
OptionalDouble rs = stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).max();
return rs.isPresent() ? (float) rs.getAsDouble() : defResult;
} else if (attr.type() == double.class || attr.type() == Double.class) {
OptionalDouble rs = stream.mapToDouble(x -> (Double) attr.get(x)).max();
return rs.isPresent() ? rs.getAsDouble() : defResult;
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
case MIN:
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
OptionalInt rs = stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).min();
return rs.isPresent() ? rs.getAsInt() : defResult;
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
OptionalLong rs = stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).min();
return rs.isPresent() ? rs.getAsLong() : defResult;
} else if (attr.type() == short.class || attr.type() == Short.class) {
OptionalInt rs = stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).min();
return rs.isPresent() ? (short) rs.getAsInt() : defResult;
} else if (attr.type() == float.class || attr.type() == Float.class) {
OptionalDouble rs = stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).min();
return rs.isPresent() ? (float) rs.getAsDouble() : defResult;
} else if (attr.type() == double.class || attr.type() == Double.class) {
OptionalDouble rs = stream.mapToDouble(x -> (Double) attr.get(x)).min();
return rs.isPresent() ? rs.getAsDouble() : defResult;
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
case SUM:
if (attr.type() == int.class || attr.type() == Integer.class || attr.type() == AtomicInteger.class) {
return stream.mapToInt(x -> ((Number) attr.get(x)).intValue()).sum();
} else if (attr.type() == long.class || attr.type() == Long.class || attr.type() == AtomicLong.class) {
return stream.mapToLong(x -> ((Number) attr.get(x)).longValue()).sum();
} else if (attr.type() == short.class || attr.type() == Short.class) {
return (short) stream.mapToInt(x -> ((Short) attr.get(x)).intValue()).sum();
} else if (attr.type() == float.class || attr.type() == Float.class) {
return (float) stream.mapToDouble(x -> ((Float) attr.get(x)).doubleValue()).sum();
} else if (attr.type() == double.class || attr.type() == Double.class) {
return stream.mapToDouble(x -> (Double) attr.get(x)).sum();
}
throw new RuntimeException("getNumberResult error(type:" + type + ", attr.declaringClass: " + attr.declaringClass() + ", attr.field: " + attr.field() + ", attr.type: " + attr.type());
}
return defResult;
}
public Sheet querySheet(final SelectColumn selects, final Flipper flipper, final FilterNode node) {
return querySheet(true, selects, flipper, node);
}
public Sheet querySheet(final boolean needtotal, final SelectColumn selects, final Flipper flipper, FilterNode node) {
final Predicate filter = node == null ? null : node.createPredicate(this);
final Comparator comparator = createComparator(flipper);
long total = 0;
if (needtotal) {
Stream stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
total = stream.count();
}
if (needtotal && total == 0) return new Sheet<>();
Stream stream = this.list.stream();
if (filter != null) stream = stream.filter(filter);
if (comparator != null) stream = stream.sorted(comparator);
if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset());
if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit());
final List rs = new ArrayList<>();
if (selects == null) {
Consumer action = x -> rs.add(needcopy ? newReproduce.apply(creator.create(), x) : x);
if (comparator != null) {
stream.forEachOrdered(action);
} else {
stream.forEach(action);
}
} else {
final List> attrs = new ArrayList<>();
info.forEachAttribute((k, v) -> {
if (selects.test(k)) attrs.add(v);
});
Consumer action = x -> {
final T item = creator.create();
for (Attribute attr : attrs) {
attr.set(item, attr.get(x));
}
rs.add(item);
};
if (comparator != null) {
stream.forEachOrdered(action);
} else {
stream.forEach(action);
}
}
if (!needtotal) total = rs.size();
return new Sheet<>(total, rs);
}
public int insert(T value) {
if (value == null) return 0;
final T rs = newReproduce.apply(this.creator.create(), value); //确保同一主键值的map与list中的对象必须共用。
T old = this.map.putIfAbsent(this.primary.get(rs), rs);
if (old == null) {
this.list.add(rs);
return 1;
} else {
logger.log(Level.WARNING, this.type + " cache repeat insert data: " + value);
return 0;
}
}
public int delete(final Serializable id) {
if (id == null) return 0;
final T rs = this.map.remove(id);
if (rs == null) return 0;
this.list.remove(rs);
return 1;
}
public Serializable[] delete(final Flipper flipper, final FilterNode node) {
if (node == null || this.list.isEmpty()) return new Serializable[0];
final Comparator comparator = createComparator(flipper);
Stream stream = this.list.stream().filter(node.createPredicate(this));
if (comparator != null) stream = stream.sorted(comparator);
if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset());
if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit());
Object[] rms = stream.toArray();
Serializable[] ids = new Serializable[rms.length];
int i = -1;
for (Object o : rms) {
final T t = (T) o;
ids[++i] = this.primary.get(t);
this.map.remove(ids[i]);
this.list.remove(t);
}
return ids;
}
public int update(final T value) {
if (value == null) return 0;
T rs = this.map.get(this.primary.get(value));
if (rs == null) return 0;
synchronized (rs) {
this.chgReproduce.apply(rs, value);
}
return 1;
}
public T update(final T value, Collection> attrs) {
if (value == null) return value;
T rs = this.map.get(this.primary.get(value));
if (rs == null) return rs;
synchronized (rs) {
for (Attribute attr : attrs) {
attr.set(rs, attr.get(value));
}
}
return rs;
}
public T[] update(final T value, final Collection> attrs, final FilterNode node) {
if (value == null || node == null) return (T[]) Array.newInstance(type, 0);
T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
for (T rs : rms) {
synchronized (rs) {
for (Attribute attr : attrs) {
attr.set(rs, attr.get(value));
}
}
}
return rms;
}
public T update(final Serializable id, Attribute attr, final V fieldValue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs != null) attr.set(rs, fieldValue);
return rs;
}
public T[] update(Attribute attr, final V fieldValue, final FilterNode node) {
if (attr == null || node == null) return (T[]) Array.newInstance(type, 0);
T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
for (T rs : rms) {
attr.set(rs, fieldValue);
}
return rms;
}
public T updateColumn(final Serializable id, List> attrs, final List values) {
if (id == null || attrs == null || attrs.isEmpty()) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
for (int i = 0; i < attrs.size(); i++) {
ColumnValue cv = values.get(i);
updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue());
}
}
return rs;
}
public T[] updateColumn(final FilterNode node, final Flipper flipper, List> attrs, final List values) {
if (attrs == null || attrs.isEmpty() || node == null) return (T[]) Array.newInstance(type, 0);
Stream stream = this.list.stream();
final Comparator comparator = createComparator(flipper);
if (comparator != null) stream = stream.sorted(comparator);
if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit());
T[] rms = stream.filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len));
for (T rs : rms) {
synchronized (rs) {
for (int i = 0; i < attrs.size(); i++) {
ColumnValue cv = values.get(i);
updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue());
}
}
}
return rms;
}
public T updateColumnOr(final Serializable id, Attribute attr, final long orvalue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
return updateColumn(attr, rs, ColumnExpress.ORR, orvalue);
}
}
public T updateColumnAnd(final Serializable id, Attribute attr, final long andvalue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
return updateColumn(attr, rs, ColumnExpress.AND, andvalue);
}
}
public T updateColumnIncrement(final Serializable id, Attribute attr, final long incvalue) {
if (id == null) return null;
T rs = this.map.get(id);
if (rs == null) return rs;
synchronized (rs) {
return updateColumn(attr, rs, ColumnExpress.INC, incvalue);
}
}
private T updateColumn(Attribute attr, final T rs, final ColumnExpress express, Serializable val) {
final Class ft = attr.type();
Number numb = null;
Serializable newval = null;
switch (express) {
case INC:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = (Number) val;
} else {
numb = numb.longValue() + ((Number) val).longValue();
}
break;
case MUL:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() * ((Number) val).floatValue();
}
break;
case AND:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() & ((Number) val).longValue();
}
break;
case ORR:
numb = (Number) attr.get(rs);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() | ((Number) val).longValue();
}
break;
case MOV:
newval = val;
break;
}
if (numb != null) {
if (ft == int.class || ft == Integer.class) {
newval = numb.intValue();
} else if (ft == long.class || ft == Long.class) {
newval = numb.longValue();
} else if (ft == short.class || ft == Short.class) {
newval = numb.shortValue();
} else if (ft == float.class || ft == Float.class) {
newval = numb.floatValue();
} else if (ft == double.class || ft == Double.class) {
newval = numb.doubleValue();
} else if (ft == byte.class || ft == Byte.class) {
newval = numb.byteValue();
} else if (ft == AtomicInteger.class) {
newval = new AtomicInteger(numb.intValue());
} else if (ft == AtomicLong.class) {
newval = new AtomicLong(numb.longValue());
}
} else {
if (ft == AtomicInteger.class && newval != null && newval.getClass() != AtomicInteger.class) {
newval = new AtomicInteger(((Number) newval).intValue());
} else if (ft == AtomicLong.class && newval != null && newval.getClass() != AtomicLong.class) {
newval = new AtomicLong(((Number) newval).longValue());
}
}
attr.set(rs, (V) newval);
return rs;
}
public Attribute getAttribute(String fieldname) {
return info.getAttribute(fieldname);
}
//-------------------------------------------------------------------------------------------------------------------------------
protected Comparator createComparator(Flipper flipper) {
if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) 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)) {
Function getter = null;
if (pattr.type() == int.class || pattr.type() == Integer.class || pattr.type() == AtomicInteger.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).intValue());
} else if (pattr.type() == long.class || pattr.type() == Long.class || pattr.type() == AtomicLong.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).longValue());
} else if (pattr.type() == float.class || pattr.type() == Float.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).floatValue());
} else if (pattr.type() == double.class || pattr.type() == Double.class) {
getter = x -> Math.abs(((Number) pattr.get((T) x)).doubleValue());
} else {
throw new RuntimeException("Flipper not supported sort illegal type by ABS (" + flipper.getSort() + ")");
}
attr = (Attribute) Attribute.create(pattr.declaringClass(), pattr.field(), pattr.type(), getter, (o, v) -> pattr.set(o, v));
} 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) {
if (node == null || node.isOr()) return false;
if (!attribute.field().equals(node.column)) return false;
if (node.nodes == null) return true;
for (FilterNode n : node.nodes) {
if (!test(n)) return false;
}
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;
}
};
}
}
}
}