All Downloads are FREE. Search and download functionalities are using the official Maven repository.

shz.core.orm.OrmService Maven / Gradle / Ivy

There is a newer version: 2024.0.2
Show newest version
package shz.core.orm;

import shz.core.*;
import shz.core.function.ActionRunner;
import shz.core.model.PageInfo;
import shz.core.orm.annotation.*;
import shz.core.orm.param.OrmConsumer;
import shz.core.orm.param.OrmFilter;
import shz.core.orm.param.OrmMapFilter;
import shz.core.orm.param.OrmMapping;
import shz.core.orm.param.OrmParam;
import shz.core.serializable.SerializableGetter;
import shz.core.serializable.Serializer;
import shz.core.st.tst.LTST;
import shz.core.structure.BloomFilter;
import shz.core.structure.FilterContainer;
import shz.core.structure.UnInfo;
import shz.core.structure.config.BigMapConfig;
import shz.core.structure.config.MapConfig;
import shz.core.structure.limiter.Limiter;
import shz.core.type.TypeHelp;
import shz.core.orm.enums.Condition;
import shz.core.orm.enums.ValueStrategy;
import shz.core.orm.param.OrmMapActionRunner;
import shz.core.orm.sql.Sql;
import shz.core.orm.sql.WhereSql;
import shz.core.orm.sql.builder.SqlBuilder;
import shz.core.orm.sql.segment.Segment;

import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.Function;

@SuppressWarnings("unchecked")
public abstract class OrmService extends AbstractDefaultOrmService {
    public static OrmService get() {
        return Container.get(OrmService.class);
    }

    public static OrmService get(String dsName) {
        OrmService service = get();
        return NullHelp.isEmpty(dsName) ? service : service.service(dsName);
    }

    protected abstract OrmService service(String dsName);

    public final Object proxyExecute(Method method, Object[] args) {
        return ActionRunner.class.isAssignableFrom(method.getReturnType()) ? proxyExecuteRunner(method, args) : proxyExecute0(method, args);
    }

    private  Object proxyExecute0(Method method, Object[] args) {
        Class returnType = method.getReturnType();
        NullHelp.requireNon(returnType == void.class || returnType == Void.class);
        Parameter[] parameters = method.getParameters();
        Update update = method.getAnnotation(Update.class);
        if (update != null) {
            Sql sql = sql(parameters, args, update.value());
            return update(sql.value(), sql.toArray());
        }
        Delete delete = method.getAnnotation(Delete.class);
        if (delete != null) {
            Sql sql = sql(parameters, args, delete.value());
            return delete(sql.value(), sql.toArray());
        }

        PageInfo pageInfo = null;
        Type type = null;
        for (int i = 0; i < parameters.length; ++i) {
            Param param = parameters[i].getAnnotation(Param.class);
            if (param == null && PageInfo.class.isAssignableFrom(parameters[i].getType())) {
                pageInfo = (PageInfo) args[i];
                Objects.requireNonNull(pageInfo);
                type = getType(parameters[i]);
                break;
            }
        }
        if (type == null) type = getType(method);

        Query query = method.getAnnotation(Query.class);
        if (query != null) {
            Sql sql = sql(parameters, args, query.value());
            if (pageInfo != null) return page(pageInfo, type, sql.value(), sql.toArray());
            if (List.class.isAssignableFrom(returnType)) return selectList(type, sql.value(), sql.toArray());
            return selectOne(type, query.unique(), sql.value(), sql.toArray());
        }

        Class entityClass = TypeHelp.toClass(type);
        WhereSql whereSql = whereSql(nonNullClassInfo(entityClass), parameters, args);
        List fieldNames;
        Fields fa = method.getAnnotation(Fields.class);
        if (fa != null && fa.value().length > 0) fieldNames = Arrays.asList(fa.value());
        else fieldNames = null;
        if (pageInfo != null) return page(pageInfo, entityClass, fieldNames, whereSql);
        if (List.class.isAssignableFrom(returnType)) return selectList(entityClass, fieldNames, whereSql);
        return selectOne(entityClass, false, fieldNames, whereSql);
    }

    private Sql sql(Parameter[] parameters, Object[] args, String sql) {
        if (parameters.length == 0) return () -> humpToUnderlineSql(sql);
        Segment.Input input = new Segment.Input();

        input.map = new HashMap<>();
        for (int i = 0; i < parameters.length; ++i) {
            Param param = parameters[i].getAnnotation(Param.class);
            if (param == null) continue;

            ValueStrategy strategy = param.strategy();
            if (strategy == ValueStrategy.NOT_NULL) {
                if (args[i] == null) continue;
            } else if (strategy == ValueStrategy.NOT_EMPTY) {
                if (NullHelp.isEmpty(args[i])) continue;
            } else if (strategy == ValueStrategy.NOT_BLANK) {
                if (NullHelp.isBlank(args[i])) continue;
            }

            String key = param.value();

            Class pt = parameters[i].getType();
            if (Collection.class.isAssignableFrom(pt) || pt.isArray() || TypeHelp.likeCommon(pt))
                input.map.put(key, args[i]);
            else fieldValueMap(input.map, args[i], key);
        }
        input.params = new LinkedList<>();
        SqlBuilder builder = builder();
        for (Segment segment : segments(sql)) segment.fill(builder, input);
        return Sql.of(builder.build(), input.params.isEmpty() ? Collections.emptyList() : input.params);
    }

    private static final LTST UNDERLINE_SQL_CACHE = LTST.of();

    private String humpToUnderlineSql(String sql) {
        char[] chars = sql.toCharArray();
        String underlineSql = UNDERLINE_SQL_CACHE.get(chars);
        if (underlineSql == null) {
            underlineSql = sqlHandler.humpToUnderlineSql(sql);
            UNDERLINE_SQL_CACHE.put(chars, underlineSql);
        }
        return underlineSql;
    }

    protected abstract Map> tableSchemaNameMap();

    private void fieldValueMap(Map map, Object obj, String mapField) {
        if (obj == null) {
            map.put(mapField, null);
            return;
        }
        if (obj instanceof PageInfo) {
            PageInfo pageInfo = (PageInfo) obj;
            pageInfo.reset();
            map.put(mapField + ".page", pageInfo.getPage());
            map.put(mapField + ".size", pageInfo.getSize());
            map.put(mapField + ".min", pageInfo.getMin());
            map.put(mapField + ".max", pageInfo.getMax());
            return;
        }

        Class cls = obj.getClass();
        if (Limiter.class.isAssignableFrom(cls) || OrmParam.class.isAssignableFrom(cls)) return;
        if (Collection.class.isAssignableFrom(cls) || cls.isArray() || TypeHelp.likeCommon(cls)) map.put(mapField, obj);
        else if (Map.class.isAssignableFrom(cls))
            ((Map) obj).forEach((k, v) -> fieldValueMap(map, v, mapField + "." + k));
        else AccessibleHelp.fields(cls).forEach(f -> {
                Object val = AccessibleHelp.getField(f, obj);
                Where where = f.getAnnotation(Where.class);
                if (where != null) {
                    ValueStrategy strategy = where.strategy();
                    if (strategy == ValueStrategy.NOT_NULL) {
                        if (val == null) return;
                    } else if (strategy == ValueStrategy.NOT_EMPTY) {
                        if (NullHelp.isEmpty(val)) return;
                    } else if (strategy == ValueStrategy.NOT_BLANK) {
                        if (NullHelp.isBlank(val)) return;
                    }
                } else if (NullHelp.isBlank(val)) return;
                fieldValueMap(map, val, mapField + "." + f.getName());
            });
    }

    private static final LTST> SQL_SEGMENTS_CACHE = LTST.of();

    private List segments(String sql) {
        char[] chars = sql.toCharArray();
        List segments = SQL_SEGMENTS_CACHE.get(chars);
        if (segments == null) {
            segments = sqlHandler.segments(sqlHandler.humpToUnderlineSql(sql));
            NullHelp.requireNonEmpty(segments, "sql异常:%s", sql);
            SQL_SEGMENTS_CACHE.put(chars, segments);
        }
        return segments;
    }

    private WhereSql whereSql(ClassInfo classInfo, Parameter[] parameters, Object[] args) {
        return WhereInfo.where(classInfo, parameters, args, this::builder);
    }

    private Type getType(Parameter parameter) {
        Type type = parameter.getParameterizedType();
        if (type instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
            if (actualTypeArguments.length > 0) return actualTypeArguments[0];
        }
        return type;
    }

    private Type getType(Method method) {
        Type type = method.getGenericReturnType();
        Class returnType = method.getReturnType();
        if (OrmMapActionRunner.class.isAssignableFrom(returnType)) return Map.class;
        if (type instanceof ParameterizedType &&
                (PageInfo.class.isAssignableFrom(returnType)
                        || List.class.isAssignableFrom(returnType)
                        || ActionRunner.class.isAssignableFrom(returnType))
        ) {
            Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
            if (actualTypeArguments.length > 0) return actualTypeArguments[0];
        }
        return type;
    }

    private  ActionRunner proxyExecuteRunner(Method method, Object[] args) {
        Class returnType = method.getReturnType();
        Parameter[] parameters = method.getParameters();

        Limiter limiter = null;
        boolean limiterMark = false;
        OrmMapping mapping = null;
        boolean mappingMark = false;
        OrmMapFilter mapFilter = null;
        boolean mapFilterMark = false;
        OrmFilter filter = null;
        boolean filterMark = false;

        int count = 0;
        Type type = null;
        for (int i = 0; i < parameters.length; ++i) {
            if (count == 4) break;
            Class pType = parameters[i].getType();
            if (!limiterMark && Limiter.class.isAssignableFrom(pType)) {
                limiter = (Limiter) args[i];
                limiterMark = true;
                ++count;
            } else if (OrmParam.class.isAssignableFrom(pType)) {
                if (!mappingMark && OrmMapping.class.isAssignableFrom(pType)) {
                    mapping = (OrmMapping) args[i];
                    mappingMark = true;
                    ++count;
                } else if (!mapFilterMark && OrmMapFilter.class.isAssignableFrom(pType)) {
                    mapFilter = (OrmMapFilter) args[i];
                    mapFilterMark = true;
                    ++count;
                } else if (!filterMark && OrmFilter.class.isAssignableFrom(pType)) {
                    filter = (OrmFilter) args[i];
                    filterMark = true;
                    ++count;
                    type = getType(parameters[i]);
                }
            }
        }
        if (type == null) type = getType(method);

        Query query = method.getAnnotation(Query.class);
        if (query != null) {
            Sql sql = sql(parameters, args, query.value());
            if (OrmMapActionRunner.class.isAssignableFrom(returnType))
                return (ActionRunner) runner(mapping, type, limiter, mapFilter, query.fetchSize(), sql.value(), sql.toArray());
            return runner(mapping, type, limiter, mapFilter, filter, query.fetchSize(), sql.value(), sql.toArray());
        }

        Class entityClass = TypeHelp.toClass(type);
        WhereSql whereSql = whereSql(nonNullClassInfo(entityClass), parameters, args);
        List fieldNames;
        Fields fa = method.getAnnotation(Fields.class);
        if (fa != null && fa.value().length > 0) fieldNames = Arrays.asList(fa.value());
        else fieldNames = null;
        if (OrmMapActionRunner.class.isAssignableFrom(returnType))
            return (ActionRunner) runner(null, entityClass, limiter, mapFilter, 0, fieldNames, whereSql);
        return runner(null, entityClass, limiter, mapFilter, filter, 0, fieldNames, whereSql);
    }

    private static final Map, Object> PROXY = new ConcurrentHashMap<>(128);

    public static  T getProxy(Class cls, OrmService service) {
        if (cls == null || !cls.isInterface() || service == null) return null;
        return (T) PROXY.computeIfAbsent(cls, k -> InterfaceProxy.getProxy(k, p -> service.proxyExecute(p.method, p.args)));
    }

    public static  T getProxy(Class cls) {
        if (cls == null || !cls.isInterface()) return null;
        Repository repository = cls.getAnnotation(Repository.class);
        return repository == null ? null : getProxy(cls, get(repository.value()));
    }

    @SafeVarargs
    public final  boolean checkUniqueForInsert(T entity, SerializableGetter... uniqueFields) {
        Class cls = (Class) entity.getClass();
        ClassInfo classInfo = nonNullClassInfo(cls);
        WhereSql whereSql = WhereInfo.uniqueWhere(classInfo, entity, this::builder, Serializer.getFieldNameArray(uniqueFields));
        return selectOne(cls, true, Collections.singletonList(classInfo.idField.getName()), whereSql) != null;
    }

    @SafeVarargs
    public final  boolean checkUniqueForUpdate(T entity, SerializableGetter... uniqueFields) {
        Class cls = (Class) entity.getClass();
        ClassInfo classInfo = nonNullClassInfo(cls);
        WhereSql whereSql = WhereInfo.uniqueWhere(classInfo, entity, this::builder, Serializer.getFieldNameArray(uniqueFields));
        T oldEntity = selectOne(cls, true, Collections.singletonList(classInfo.idField.getName()), whereSql);
        return oldEntity != null && !Objects.equals(classInfo.getId(entity), classInfo.getId(oldEntity));
    }

    @SafeVarargs
    public final  boolean batchEdit(List newDataset, List oldDataset, Function... classifiers) {
        T entity = null;
        if (NullHelp.nonEmpty(newDataset)) entity = newDataset.get(0);
        else if (NullHelp.nonEmpty(oldDataset)) entity = oldDataset.get(0);
        if (entity == null) return false;
        ClassInfo classInfo = nonNullClassInfo(entity.getClass());
        UnInfo unInfo = UnInfo.of(newDataset, oldDataset, classInfo::getId, classInfo::setId, classifiers);
        return batchFail(batchDeleteById(unInfo.getDelete())) || batchFail(batchUpdateById(unInfo.getUpdate())) || batchFail(batchInsert(unInfo.getInsert()));
    }

    public final  Set existsId(Tnp tnp, Class entityClass, Collection ids, Boolean logic) {
        if (NullHelp.isEmpty(ids)) return Collections.emptySet();
        ClassInfo classInfo = nonNullClassInfo(entityClass);
        WhereSql whereSql = whereSql(classInfo, classInfo.idField.getName(), ids, Condition.IN, logic);
        Set exists = ToSet.get(ids.size()).build();
        query(tnp, entityClass, null, null, map -> exists.add((ID) map.get(classInfo.idField.getName())), Integer.MAX_VALUE, Collections.singletonList(classInfo.idField.getName()), whereSql);
        return exists;
    }

    public final  Set existsId(Class entityClass, Collection ids, Boolean logic) {
        return existsId(null, entityClass, ids, logic);
    }

    public final  Set existsId(Class entityClass, Collection ids) {
        return existsId(null, entityClass, ids, null);
    }

    public final  Map existsColumn(Tnp tnp, Class entityClass, String fieldName, Collection fieldValues, Boolean logic) {
        if (NullHelp.isBlank(fieldName) || NullHelp.isEmpty(fieldValues)) return Collections.emptyMap();
        ClassInfo classInfo = nonNullClassInfo(entityClass);
        WhereSql whereSql = whereSql(classInfo, fieldName, fieldValues, Condition.IN, logic);
        Map exists = ToMap.get(fieldValues.size()).build();
        query(tnp, entityClass, null, null, map -> exists.put((V) map.get(fieldName), (ID) map.get(classInfo.idField.getName())), Integer.MAX_VALUE, Arrays.asList(classInfo.idField.getName(), fieldName), whereSql);
        return exists;
    }

    public final  Map existsColumn(Class entityClass, String fieldName, Collection fieldValues, Boolean logic) {
        return existsColumn(null, entityClass, fieldName, fieldValues, logic);
    }

    public final  Map existsColumn(Class entityClass, String fieldName, Collection fieldValues) {
        return existsColumn(null, entityClass, fieldName, fieldValues, null);
    }

    @SafeVarargs
    public final  void replace(Tnp tnp, List entities, List insertFieldNames, OrmMapFilter mapFilter, OrmFilter filter, OrmConsumer existed, OrmConsumer beforeDelete, int fetchSize, Executor executor, List fieldNames, WhereSql whereSql, Boolean logic, SerializableGetter... uniqueFields) {
        if (NullHelp.isAnyNull(entities)) return;
        Class cls = (Class) entities.get(0).getClass();
        ClassInfo classInfo = nonNullClassInfo(cls);
        batchInsertOrUpdate(tnp, entities, insertFieldNames, logic, 0, Serializer.getFieldNameArray(uniqueFields));
        Set ids = ToSet.explicitCollect(entities.stream().map(classInfo::getId), entities.size());
        ActionRunner runner = runner(tnp, cls, null, mapFilter, entity -> {
            if (filter != null && !filter.test(entity)) return false;
            if (ids.contains(classInfo.getId(entity))) {
                if (existed != null) existed.accept(entity);
                return false;
            }
            if (beforeDelete != null) beforeDelete.accept(entity);
            return true;
        }, fetchSize, fieldNames, whereSql);
        delete(runner, logic, executor);
    }

    @SafeVarargs
    public final  void replace(List entities, List insertFieldNames, OrmMapFilter mapFilter, OrmFilter filter, OrmConsumer existed, OrmConsumer beforeDelete, int fetchSize, Executor executor, List fieldNames, WhereSql whereSql, Boolean logic, SerializableGetter... uniqueFields) {
        replace(null, entities, insertFieldNames, mapFilter, filter, existed, beforeDelete, fetchSize, executor, fieldNames, whereSql, logic, uniqueFields);
    }

    @SafeVarargs
    public final  void replace(List entities, List insertFieldNames, OrmMapFilter mapFilter, OrmFilter filter, OrmConsumer existed, OrmConsumer beforeDelete, WhereSql whereSql, SerializableGetter... uniqueFields) {
        replace(null, entities, insertFieldNames, mapFilter, filter, existed, beforeDelete, 0, null, null, whereSql, null, uniqueFields);
    }

    @SafeVarargs
    public final  void replace(List entities, OrmMapFilter mapFilter, OrmFilter filter, OrmConsumer existed, OrmConsumer beforeDelete, WhereSql whereSql, SerializableGetter... uniqueFields) {
        replace(null, entities, null, mapFilter, filter, existed, beforeDelete, 0, null, null, whereSql, null, uniqueFields);
    }

    public final  void copyTo(OrmMapping mapping, OrmService desService, Tnp desTnp, Class desEntityClass, List desFieldNames, Limiter limiter, OrmMapFilter mapFilter, OrmFilter filter, int fetchSize, Executor executor, boolean hasId, String srcSql, Object... params) {
        ClassInfo classInfo = desService.nonNullClassInfo(desEntityClass);
        int count = count(builder().append(srcSql).count(), params);
        if (count < 1) return;
        OrmMapFilter mapFilter0;
        if (hasId) {
            FilterContainer container;
            Class type = classInfo.idField.getType();
            if (type == Long.class || type == long.class) container = new BigMapConfig();
            else if (type == Integer.class || type == int.class) container = new MapConfig();
            else container = BloomFilter.of(count);
            mapFilter0 = map -> {
                Object id = map.get(classInfo.idName);
                if (id == null) return false;
                if (container.contain(id)) return false;
                else container.push(id);
                return mapFilter == null || mapFilter.test(map);
            };
        } else mapFilter0 = mapFilter;
        boolean force = NullHelp.nonEmpty(desFieldNames);
        desService.accept(() -> {
            ActionRunner runner = runner(mapping, desEntityClass, limiter, mapFilter0, filter, fetchSize, srcSql, params);
            Runner.run(runner, entities -> desService.batchInsert(desTnp, MetaObject.of(entities, classInfo, desFieldNames), -1, force), null, -1, executor);
        });
    }

    public final  void copyTo(OrmMapping mapping, OrmService desService, Tnp desTnp, Class desEntityClass, List desFieldNames, Limiter limiter, OrmMapFilter mapFilter, OrmFilter filter, Tnp srcTnp, Class srcEntityClass, int fetchSize, Executor executor, boolean hasId, List srcFieldNames, WhereSql srcWhereSql) {
        Sql sql = querySql(srcTnp, nonNullClassInfo(srcEntityClass), srcFieldNames, srcWhereSql);
        if (sql != null)
            copyTo(mapping, desService, desTnp, desEntityClass, desFieldNames, limiter, mapFilter, filter, fetchSize, executor, hasId, sql.value(), sql.toArray());
    }

    public final  void copyOrUpdateTo(OrmMapping mapping, OrmService desService, Tnp desTnp, Class desEntityClass, List desFieldNames, Boolean desLogic, Limiter limiter, OrmMapFilter mapFilter, OrmFilter filter, String desMatchFieldName, int fetchSize, Executor executor, String srcSql, Object... params) {
        ClassInfo classInfo = desService.nonNullClassInfo(desEntityClass);
        int count = count(builder().append(srcSql).count(), params);
        if (count < 1) return;
        boolean hasId = NullHelp.isBlank(desMatchFieldName);
        Field matchField;
        String matchColumn;
        if (hasId) {
            matchField = classInfo.idField;
            matchColumn = classInfo.idName;
        } else {
            matchField = classInfo.getFieldByName(desMatchFieldName);
            matchColumn = classInfo.toColumnName(desMatchFieldName);
            NullHelp.requireNon(matchField == null || matchColumn == null, "匹配的域:%s不存在", desMatchFieldName);
        }
        boolean force = NullHelp.nonEmpty(desFieldNames);
        desService.accept(() -> {
            ActionRunner runner = runner(mapping, desEntityClass, limiter, map -> {
                Object matchValue = map.get(matchColumn);
                if (matchValue == null) return false;
                return mapFilter == null || mapFilter.test(map);
            }, filter, fetchSize, srcSql, params);
            Runner.run(runner, entities -> {
                Map map = ToMap.explicitCollect(entities.stream(), entity -> AccessibleHelp.getField(matchField, entity), Function.identity(), entities.size());
                if (hasId) {
                    Set ids = desService.existsId(desTnp, desEntityClass, map.keySet(), desLogic);
                    if (ids.isEmpty())
                        desService.batchInsert(desTnp, MetaObject.of(entities, classInfo, desFieldNames), -1, force);
                    else {
                        desService.batchUpdateById(desTnp, MetaObject.of(ToList.explicitCollect(ids.stream().map(map::get), ids.size()), classInfo, desFieldNames), -1, force);
                        int delta = map.size() - ids.size();
                        if (delta > 0)
                            desService.batchInsert(desTnp, MetaObject.of(ToList.explicitCollect(map.keySet().stream().filter(id -> !ids.contains(id)).map(map::get), delta), classInfo, desFieldNames), -1, force);
                    }
                } else {
                    Map columnIds = desService.existsColumn(desTnp, desEntityClass, desMatchFieldName, map.keySet(), desLogic);
                    if (columnIds.isEmpty())
                        desService.batchInsert(desTnp, MetaObject.of(entities, classInfo, desFieldNames), -1, force);
                    else {
                        desService.batchUpdateById(desTnp, MetaObject.of(ToList.explicitCollect(columnIds.keySet().stream().map(column -> {
                            T entity = map.get(column);
                            classInfo.setId(entity, columnIds.get(column));
                            return entity;
                        }), columnIds.size()), classInfo, desFieldNames), -1, force);
                        int delta = map.size() - columnIds.size();
                        if (delta > 0)
                            desService.batchInsert(desTnp, MetaObject.of(ToList.explicitCollect(map.keySet().stream().filter(column -> !columnIds.containsKey(column)).map(map::get), delta), classInfo, desFieldNames), -1, force);
                    }
                }
            }, null, -1, executor);
        });
    }

    public final  void copyOrUpdateTo(OrmMapping mapping, OrmService desService, Tnp desTnp, Class desEntityClass, List desFieldNames, Boolean desLogic, Limiter limiter, OrmMapFilter mapFilter, OrmFilter filter, String desMatchFieldName, Tnp srcTnp, Class srcEntityClass, int fetchSize, Executor executor, List srcFieldNames, WhereSql srcWhereSql) {
        Sql sql = querySql(srcTnp, nonNullClassInfo(srcEntityClass), srcFieldNames, srcWhereSql);
        if (sql != null)
            copyOrUpdateTo(mapping, desService, desTnp, desEntityClass, desFieldNames, desLogic, limiter, mapFilter, filter, desMatchFieldName, fetchSize, executor, sql.value(), sql.toArray());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy