
win.doyto.query.core.MemoryDataAccess Maven / Gradle / Ivy
package win.doyto.query.core;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import win.doyto.query.annotation.NestedQueries;
import win.doyto.query.annotation.SubQuery;
import win.doyto.query.entity.CommonEntity;
import win.doyto.query.entity.Persistable;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import static win.doyto.query.core.CommonUtil.*;
import static win.doyto.query.core.QuerySuffix.*;
/**
* MemoryDataAccess
*
* @author f0rb
*/
@Slf4j
@SuppressWarnings("unchecked")
public class MemoryDataAccess, I extends Serializable, Q> implements DataAccess {
protected static final Map, Map> tableMap = new ConcurrentHashMap<>();
protected final Map entitiesMap = new ConcurrentHashMap<>();
private final AtomicLong idGenerator = new AtomicLong(0);
private final List fields;
private final Field idField;
private Class> idFieldType;
public MemoryDataAccess(Class entityClass) {
tableMap.put(entityClass, entitiesMap);
// init fields
Field[] allFields = FieldUtils.getAllFields(entityClass);
List tempFields = new ArrayList<>(allFields.length);
Arrays.stream(allFields).filter(field -> !ignoreField(field)).forEachOrdered(tempFields::add);
fields = Collections.unmodifiableList(tempFields);
Field[] idFields = FieldUtils.getFieldsWithAnnotation(entityClass, Id.class);
if (idFields.length == 1 && idFields[0].isAnnotationPresent(GeneratedValue.class)) {
idField = idFields[0];
if (CommonEntity.class.isAssignableFrom(entityClass)) {
ParameterizedType parameterizedType = (ParameterizedType) entityClass.getGenericSuperclass();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
idFieldType = (Class>) actualTypeArguments[0];
} else {
idFieldType = idField.getType();
}
} else {
idField = null;
}
}
protected void generateNewId(E entity) {
try {
Object newId = chooseIdValue(idGenerator.incrementAndGet(), idFieldType);
writeField(idField, entity, newId);
} catch (Exception e) {
log.warn("写入id失败: {} - {}", entity.getClass(), e.getMessage());
}
}
private Object chooseIdValue(Long newId, Class> type) {
Object t = newId;
if (type.isAssignableFrom(Integer.class)) {
t = newId.intValue();
}
return t;
}
@Override
public E get(I id) {
return entitiesMap.get(id);
}
@Override
public E fetch(E e) {
return SerializationUtils.clone(entitiesMap.get(e.getId()));
}
@Override
public List queryIds(Q query) {
return queryColumns(query, new SingleColumnRowMapper<>(), "id");
}
@Override
public void create(E e) {
if (idField != null) {
generateNewId(e);
}
entitiesMap.put(e.getId(), e);
}
@Override
public int update(E e) {
return entitiesMap.put(e.getId(), e) == null ? 0 : 1;
}
@Override
public int patch(E patch) {
E origin = get(patch.getId());
if (origin == null) {
return 0;
}
for (Field field : fields) {
Object value = readField(field, patch);
if (value != null) {
writeField(field, origin, value);
}
}
return 1;
}
@Override
public int patch(E p, Q q) {
List list = query(q);
for (E origin : list) {
p.setId(origin.getId());
patch(p);
}
return list.size();
}
@Override
public int delete(I id) {
return entitiesMap.remove(id) == null ? 0 : 1;
}
@Override
public int delete(Q query) {
List list = query(query);
list.stream().map(Persistable::getId).forEach(entitiesMap::remove);
return list.size();
}
/**
* 根据Query对象筛选符合条件的Entity对象
*
* @param query Query
* @param entity Entity
* @return true, Entity符合条件需要保留; false, Entity不符合条件需要过滤掉
*/
protected boolean filterByQuery(Q query, E entity) {
for (Field field : query.getClass().getDeclaredFields()) {
if (!ignoreField(field) && supportFilter(field)) {
Object v1 = readField(field, query);
if (isValidValue(v1, field)) {
boolean shouldNotRemain = unsatisfied(entity, field.getName(), v1);
if (shouldNotRemain) {
return false;
}
}
}
}
return true;
}
private boolean supportFilter(Field field) {
return !field.isAnnotationPresent(SubQuery.class) && !field.isAnnotationPresent(NestedQueries.class);
}
protected Boolean unsatisfied(E entity, String queryFieldName, Object queryFieldValue) {
QuerySuffix querySuffix = resolve(queryFieldName);
String columnName = querySuffix.resolveColumnName(queryFieldName);
FilterExecutor.Matcher matcher = FilterExecutor.get(querySuffix);
if (columnName.contains("Or")) {
String[] names = splitByOr(columnName);
return Arrays.stream(names).
map(name -> readField(entity, camelize(name))).
noneMatch(entityFieldValue -> matcher.match(queryFieldValue, entityFieldValue));
} else {
Object entityFieldValue = readField(entity, columnName);
return !matcher.match(queryFieldValue, entityFieldValue);
}
}
@Override
public List query(Q query) {
List queryList = entitiesMap
.values().stream()
.filter(item -> filterByQuery(query, item))
.collect(Collectors.toList());
if (query instanceof PageQuery) {
PageQuery pageQuery = (PageQuery) query;
if (pageQuery.getSort() != null) {
doSort(queryList, pageQuery.getSort());
}
if (pageQuery.needPaging()) {
int from = pageQuery.getPageNumber() * pageQuery.getPageSize();
int end = Math.min(queryList.size(), from + pageQuery.getPageSize());
if (from <= end) {
queryList = new ArrayList<>(queryList.subList(from, end));
}
}
}
return queryList;
}
@Override
@SneakyThrows
public List queryColumns(Q q, RowMapper rowMapper, String... columns) {
List entities = query(q);
List objects = new ArrayList<>(entities.size());
if (rowMapper instanceof SingleColumnRowMapper) {
return entities.stream().map(entity -> (V) readField(entity, columns[0])).collect(Collectors.toList());
} else {
Class classV = (Class) readField(rowMapper, "mappedClass");
for (E e : entities) {
V v = classV.getDeclaredConstructor().newInstance();
BeanUtils.copyProperties(e, v);
objects.add(v);
}
}
return objects;
}
protected void doSort(List queryList, String sort) {
String[] orders = sort.split(";");
for (int i = orders.length - 1; i >= 0; i--) {
String order = orders[i];
queryList.sort((o1, o2) -> {
String[] pd = order.split(",");
String property = toCamelCase(pd[0]);
Comparable c1 = (Comparable) readField(o1, property);
Object c2 = readField(o2, property);
int ret = c1.compareTo(c2);
return "asc".equalsIgnoreCase(pd[1]) ? ret : -ret;
});
}
}
@Override
public long count(Q query) {
return entitiesMap.values().stream().filter(item -> filterByQuery(query, item)).count();
}
private static class FilterExecutor {
static final Map map = new EnumMap<>(QuerySuffix.class);
static class LikeMatcher implements Matcher {
@Override
public boolean doMatch(Object qv, Object ev) {
return StringUtils.contains(((String) ev), (String) qv);
}
@Override
public boolean isComparable(Object qv, Object ev) {
return ev instanceof String;
}
}
static class NotLikeMatcher extends LikeMatcher {
@Override
public boolean doMatch(Object qv, Object ev) {
return !super.doMatch(qv, ev);
}
}
static class StartMatcher extends LikeMatcher {
@Override
public boolean doMatch(Object qv, Object ev) {
return StringUtils.startsWith(((String) ev), (String) qv);
}
}
static class NotNullMatcher implements Matcher {
@Override
public boolean doMatch(Object qv, Object ev) {
return ev != null;
}
@Override
public boolean isComparable(Object qv, Object ev) {
return true;
}
}
static class NullMatcher extends NotNullMatcher {
@Override
public boolean doMatch(Object qv, Object ev) {
return !super.doMatch(qv, ev);
}
}
static {
map.put(Like, new LikeMatcher());
map.put(NotLike, new NotLikeMatcher());
map.put(Start, new StartMatcher());
map.put(Null, new NullMatcher());
map.put(NotNull, new NotNullMatcher());
map.put(In, (qv, ev) -> ((Collection) qv).contains(ev));
map.put(NotIn, (qv, ev) -> !((Collection) qv).contains(ev));
map.put(Gt, (qv, ev) -> ((Comparable) ev).compareTo(qv) > 0);
map.put(Lt, (qv, ev) -> ((Comparable) ev).compareTo(qv) < 0);
map.put(Ge, (qv, ev) -> ((Comparable) ev).compareTo(qv) >= 0);
map.put(Le, (qv, ev) -> ((Comparable) ev).compareTo(qv) <= 0);
map.put(Not, (qv, ev) -> !qv.equals(ev));
map.put(NONE, Object::equals);
}
static Matcher get(QuerySuffix querySuffix) {
return map.get(querySuffix);
}
interface Matcher {
/**
* 实体对象筛选
*
* @param qv 查询对象字段值
* @param ev 实体对象字段值
* @return true 符合过滤条件
*/
boolean doMatch(Object qv, Object ev);
default boolean match(Object qv, Object ev) {
return isComparable(qv, ev) && doMatch(qv, ev);
}
default boolean isComparable(Object qv, Object ev) {
return qv instanceof Collection || (qv instanceof Comparable && ev instanceof Comparable);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy