package com.ibyte.common.core.query; import com.alibaba.fastjson.util.TypeUtils; import com.ibyte.common.core.constant.QueryConstant.Direction; import com.ibyte.common.core.constant.QueryConstant.Operator; import com.ibyte.common.core.dto.IViewObject; import com.ibyte.common.core.dto.QueryRequest; import com.ibyte.common.core.dto.QueryResult; import com.ibyte.common.core.entity.IEntity; import com.ibyte.common.core.query.spi.QueryFilter; import com.ibyte.common.core.query.support.*; import com.ibyte.common.core.query.support.QueryFilterChain.FilterInfo; import com.ibyte.common.core.util.EntityUtil; import com.ibyte.common.core.util.PropertyLangUtil; import com.ibyte.common.exception.ParamsNotValidException; import com.ibyte.common.util.*; import com.ibyte.framework.meta.MetaConstant; import com.ibyte.framework.meta.MetaConstant.ShowType; import com.ibyte.framework.meta.MetaEntity; import com.ibyte.framework.meta.MetaProperty; import org.apache.commons.lang3.StringUtils; import org.hibernate.query.Query; import org.springframework.beans.BeanUtils; import javax.persistence.EntityManager; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import java.beans.PropertyDescriptor; import java.lang.reflect.*; import java.util.*; import java.util.Map.Entry; import java.util.function.BiFunction; /** * 通用查询模板 * * @author <a href="mailto:[email protected]">iByte</a> * * @param <E> * @param <V> */ public class PageQueryTemplate<E extends IEntity, V> { private EntityManager entityManager; private Class<E> entityClass; private Class<V> viewClass; private boolean isViewObject; private List<FilterInfo> filters = new ArrayList<>(); private boolean cacheable; private boolean filterTenant = true; private boolean langSupport = LangUtil.isSuportEnabled(); public PageQueryTemplate(EntityManager entityManager, Class<E> entityClass, Class<V> viewClass) { super(); this.entityManager = entityManager; this.entityClass = entityClass; this.viewClass = viewClass; this.isViewObject = IViewObject.class.isAssignableFrom(viewClass); } /** * 若viewClass未实现IViewObject的接口,但又想按自动接收参数,请设置为true */ public PageQueryTemplate<E, V> setIsViewObject(boolean isViewObject) { this.isViewObject = isViewObject; return this; } /** * 自定义过滤器 */ public PageQueryTemplate<E, V> addFilter(QueryFilter filter, Object param) { filters.add(new FilterInfo(filter, param)); return this; } /** * 自定义过滤器 */ public PageQueryTemplate<E, V> addFilter( BiFunction<QueryContext, QueryFilterChain, Predicate> filter) { filters.add(new FilterInfo(filter)); return this; } /** * 是否使用二级缓存 */ public PageQueryTemplate<E, V> setCacheable(boolean cacheable) { this.cacheable = cacheable; return this; } /** * 默认为true:自动过滤租户 */ public PageQueryTemplate<E, V> setFilterTenant(boolean filterTenant) { this.filterTenant = filterTenant; return this; } /** * 执行查询 */ public QueryResult<V> findAll(QueryRequest request) { boolean filterTenant = this.filterTenant && TenantUtil.getTenantId() != TenantUtil.SYSTEM_TENANT; request.format(); // 获取总数 long count = 0; List<V> content = null; if (request.isCount()) { count = queryCount(request, filterTenant); if (count == 0) { return QueryResult.empty(); } } // 获取详情 if (request.isContent()) { if (isViewObject) { content = queryContentAndToVO(request, filterTenant); } else { content = queryContent(request, filterTenant); } } // 返回数据 QueryResult<V> queryResult = new QueryResult<>(content, request.getOffset(), request.getPageSize(), count); //TODO 日志处理 return queryResult; } private <R> TypedQuery<R> createQuery(CriteriaQuery<R> query) { TypedQuery<R> typedQuery = entityManager.createQuery(query); if (cacheable && typedQuery instanceof Query) { ((Query<R>) typedQuery).setCacheable(cacheable); } return typedQuery; } /** * 查询总数 */ private long queryCount(QueryRequest request, boolean filterTenant) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<Long> query = builder.createQuery(Long.class); QueryContextImpl<E, Long> context = new QueryContextImpl<>(request, entityClass, builder, query, filterTenant); // where applyRequestToWhere(context); // select if (query.isDistinct()) { query.select(builder.countDistinct(context.getRoot())); } else { query.select(builder.count(context.getRoot())); } // query return createQuery(query).getSingleResult(); } /** * 查询内容 */ private List<V> queryContent(QueryRequest request, boolean filterTenant) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<V> query = builder.createQuery(viewClass); QueryContextImpl<E, V> context = new QueryContextImpl<>(request, entityClass, builder, query, filterTenant); // where applyRequestToWhere(context); // order by applyRequestToOrderby(context); // select if (viewClass != entityClass) { List<String> columns = request.getColumns(); if (ArrayUtil.isEmpty(columns)) { throw new ParamsNotValidException("QueryRequest的columns参数不能为空"); } List<Selection<?>> list = new ArrayList<>(columns.size()); for (String column : columns) { list.add(context.leftJoinPath(column)); } query.multiselect(list); } // query return executeQuery(request, query); } /** * 查询内容并转换成ViewClass */ private List<V> queryContentAndToVO(QueryRequest request, boolean filterTenant) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<Tuple> query = builder.createQuery(Tuple.class); QueryContextImpl<E, Tuple> context = new QueryContextImpl<>(request, entityClass, builder, query, filterTenant); // where applyRequestToWhere(context); // order by applyRequestToOrderby(context); // select ViewInfo root = new ViewInfo(); Map<String, List<MetaProperty>> collectionProps = new HashMap<>(16); applyRequestToSelect4VO(context, root, collectionProps); // 查询并转换VO对象 List<Tuple> tuples = executeQuery(request, query); List<V> result = new ArrayList<>(tuples.size()); for (Tuple tuple : tuples) { result.add(toViewObject(tuple, root, viewClass, null)); } // 若需要查询列表属性,则再次查询 if (!collectionProps.isEmpty() && !result.isEmpty()) { List<String> ids = new ArrayList<>(tuples.size()); for (Tuple tuple : tuples) { ids.add((String) tuple.get(0)); } for (Entry<String, List<MetaProperty>> entry : collectionProps .entrySet()) { queryCollectionPropAndToVO(entry.getKey(), entry.getValue(), result, ids, filterTenant); } } return result; } /** * 处理where条件 */ private <T> void applyRequestToWhere(QueryContextImpl<E, T> context) { List<FilterInfo> filters = new ArrayList<>(); // 多租户过滤优先级最高 if (filterTenant) { filters.add(new FilterInfo(TenantQueryFilter.getInstance(), null)); } // 优先request定义的通用过滤器 Map<String, Object> requestFilters = context.getRequest().getFilters(); if (requestFilters != null) { for (Entry<String, Object> entry : requestFilters.entrySet()) { QueryFilter filter = QueryFilterManager .getFilter(entry.getKey()); if (filter == null) { throw new ParamsNotValidException( "未知的过滤器:" + entry.getKey()); } filters.add(new FilterInfo(filter, entry.getValue())); } } // 然后是自定义的过滤器 filters.addAll(this.filters); // 最后是request的condition解析器 filters.add(new FilterInfo(QueryRequestConditionParser.INSTANCE, context.getRequest().getConditions())); // 执行过滤链 QueryFilterChain chain = new QueryFilterChain(filters); Predicate predicate = chain.toPredicate(context); if (predicate != null) { context.getQuery().where(predicate); } } /** * 处理order by */ private <T> void applyRequestToOrderby(QueryContextImpl<E, T> context) { Map<String, String> sorts = context.getRequest().getSorts(); if (sorts != null) { List<Order> orders = new ArrayList<>(sorts.size()); for (Entry<String, String> entry : sorts.entrySet()) { Expression<?> expression = context.leftJoinPath(entry.getKey()); Class<?> type = expression.getJavaType(); if (type != null && IEntity.class.isAssignableFrom(type)) { // 对象类型,用displayProperty排序 MetaEntity entity = MetaEntity.localEntity(type.getName()); if (entity != null && StringUtils .isNotBlank(entity.getDisplayProperty())) { String displayName = StringHelper.join(entry.getKey(), '.', entity.getDisplayProperty()); expression = context.leftJoinPath(displayName); } } if (Direction.DESC.name().equalsIgnoreCase(entry.getValue())) { orders.add(context.getBuilder().desc(expression)); } else { orders.add(context.getBuilder().asc(expression)); } } context.getQuery().orderBy(orders); } } /** * 处理VO查询中的select */ private void applyRequestToSelect4VO(QueryContextImpl<E, Tuple> context, ViewInfo root, Map<String, List<MetaProperty>> collectionProps) { List<String> columns = context.getRequest().getColumns(); List<Selection<?>> selects = new ArrayList<>(); MetaEntity meta = MetaEntity.localEntity(entityClass.getName()); // fdId必须加在第一列 selects.add(context.getRoot().get("fdId")); if (ArrayUtil.isEmpty(columns)) { // 未指定列,查询所有列表可显示的列 for (MetaProperty prop : meta.getProperties().values()) { if (prop.getShowType() == ShowType.ALWAYS && !prop.isCollection()) { List<MetaProperty> props = new ArrayList<>(); props.add(prop); appendSelectColumn4VO(context, prop.getName(), props, selects, root); } } } else { // 指定列,查询所有列表可显示的列,支持a.b.c的格式,但不能是双重数组 outloop: for (String column : columns) { List<MetaProperty> props = EntityUtil.getMetaProperty(meta, column); boolean isCollection = false; for (MetaProperty prop : props) { if (prop.isCollection()) { if (isCollection) { throw new ParamsNotValidException("查询列不支持双重数组"); } isCollection = true; if (prop.getShowType() == ShowType.NONE) { continue outloop; } } } if (isCollection) { collectionProps.put(column, props); } else { appendSelectColumn4VO(context, column, props, selects, root); } } } context.getQuery().multiselect(selects); } /** * 添加选择列,支持a.b.c,仅用于转vo的情况 */ private void appendSelectColumn4VO(QueryContextImpl<E, Tuple> context, String propName, List<MetaProperty> props, List<Selection<?>> selects, ViewInfo root) { Class<?> clazz = viewClass; ViewInfo current = root; int n = props.size() - 1; // 层级遍历 for (int i = 0; i < props.size(); i++) { MetaProperty prop = props.get(i); PropertyDescriptor desc = BeanUtils.getPropertyDescriptor(clazz, prop.getName()); if (desc == null || desc.getWriteMethod() == null || desc.getReadMethod() == null) { throw new ParamsNotValidException( StringHelper.join(propName, "对应的VO的属性不支持读/写")); } ViewInfo child = current.getOrAddChild(desc); current = child; if (prop.isCollection()) { // 列表对象 child.arrayElementType = getListActualType(clazz, desc); if (child.arrayElementType == null) { throw new ParamsNotValidException( StringHelper.join(propName, "无法获取列表的实际类型")); } clazz = child.arrayElementType; } else { // 单值对象 clazz = desc.getPropertyType(); } if (i == n) { // 最后一级 if (MetaConstant.isAssociation(prop.getType())) { // c是子对象,遍历对象下的属性 MetaEntity meta = MetaEntity.localEntity(prop.getType()); if (meta == null) { return; } PropertyDescriptor[] childDescs = BeanUtils .getPropertyDescriptors(clazz); for (PropertyDescriptor childDesc : childDescs) { if (childDesc.getWriteMethod() == null) { continue; } // 属性必须有在数据库中,并且不能是外键对象,显示类型允许在列表展现 String name = childDesc.getName(); MetaProperty childProp = meta.getProperty(name); if (childProp == null || childProp.getShowType() != ShowType.ALWAYS || MetaConstant .isAssociation(childProp.getType())) { continue; } // 追加子对象属性 child = current.getOrAddChild(childDesc); appendSelectAndLangColumn(context, childProp, StringHelper.join(propName, '.', name), selects, child); } } else { // c是普通属性,直接追加 if ("fdId".equals(propName)) { current.index = 0; } else { appendSelectAndLangColumn(context, prop, propName, selects, current); } } } } } /** 添加选择字段,包括多语言 */ private void appendSelectAndLangColumn(QueryContextImpl<E, Tuple> context, MetaProperty prop, String propName, List<Selection<?>> selects, ViewInfo current) { current.index = selects.size(); Path<?> path = context.leftJoinPath(propName); selects.add(path); if (langSupport && prop.isLangSupport()) { String langName = PropertyLangUtil .getPropertyNameByLanguage(propName); if (langName != null) { if (langName.contains(".")) { langName = langName.substring(langName.lastIndexOf(".") + 1); } current.langIndex = selects.size(); selects.add(path.getParentPath().get(langName)); } } } /** * 转VO对象 */ private <T> T toViewObject(Tuple tuple, ViewInfo viewInfo, Class<T> clazz, T vo) { for (Entry<String, ViewInfo> entry : viewInfo.children.entrySet()) { ViewInfo child = entry.getValue(); Object value = null; if (child.arrayElementType != null && child.children != null) { // 数组,先从VO中获取列表,若列表为空则创建,然后往列表中追加子 List<Object> list = null; if (vo == null) { vo = ReflectUtil.newInstance(clazz); } else { list = readProperty(vo, child.desc); } if (list == null) { list = new ArrayList<>(); writeProperty(vo, child.desc, list); } value = toViewObject(tuple, child, child.arrayElementType, null); if(value != null) { list.add(value); } } else { if (child.children != null) { // 子对象 value = toViewObject(tuple, child, child.desc.getPropertyType(), null); } else if (child.index > -1) { // 普通字段 value = tuple.get(child.index); value = TypeUtils.cast(value, child.desc.getPropertyType(), null); if (child.langIndex > -1) { Object langValue = tuple.get(child.langIndex); langValue = TypeUtils.cast(langValue, child.desc.getPropertyType(), null); if (StringUtils.isNotBlank((String) langValue)) { value = langValue; } } } if (value != null) { if (vo == null) { vo = ReflectUtil.newInstance(clazz); } writeProperty(vo, child.desc, value); } } } return vo; } /** * 查询列表属性,并追加到结果中 */ private void queryCollectionPropAndToVO(String column, List<MetaProperty> props, List<V> result, List<String> ids, boolean filterTenant) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<Tuple> query = builder.createQuery(Tuple.class); QueryContextImpl<E, Tuple> context = new QueryContextImpl<>(null, entityClass, builder, query, filterTenant); query.where(context.toPredicate("fdId", Operator.eq, ids)); // select ViewInfo root = new ViewInfo(); List<Selection<?>> selects = new ArrayList<>(); selects.add(context.getRoot().get("fdId")); appendSelectColumn4VO(context, column, props, selects, root); query.multiselect(selects); // 查询并转换VO对象 List<Tuple> tuples = createQuery(query).getResultList(); for (Tuple tuple : tuples) { String id = (String) tuple.get(0); for (int i = 0; i < ids.size(); i++) { if (id.equals(ids.get(i))) { toViewObject(tuple, root, viewClass, result.get(i)); break; } } } } /** * 执行查询 */ private <T> List<T> executeQuery(QueryRequest request, CriteriaQuery<T> query) { TypedQuery<T> typedQuery = createQuery(query); typedQuery.setFirstResult(request.getOffset()); typedQuery.setMaxResults(request.getPageSize()); return typedQuery.getResultList(); } /** * 读取列表元素类型 */ private Class<?> getListActualType(Class<?> clazz, PropertyDescriptor desc) { Method read = desc.getReadMethod(); Type type = read.getGenericReturnType(); if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) type; Type[] types = paramType.getActualTypeArguments(); if (Collection.class .isAssignableFrom((Class<?>) paramType.getRawType()) && types != null && types.length > 0) { Type elemType = types[0]; if (elemType instanceof Class) { return (Class<?>) elemType; } else if (elemType instanceof TypeVariable) { return ReflectUtil.getActualClass(clazz, read.getDeclaringClass(), ((TypeVariable<?>) elemType).getName()); } } } return null; } /** * 读取bean中的值 */ @SuppressWarnings("unchecked") private <T> T readProperty(Object bean, PropertyDescriptor desc) { try { return (T) desc.getReadMethod().invoke(bean); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } } /** * 往bean中写值 */ private void writeProperty(Object bean, PropertyDescriptor desc, Object value) { try { desc.getWriteMethod().invoke(bean, new Object[] { value }); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } } /** * VO对象信息 * * @author <a href="mailto:[email protected]">iByte</a> */ static class ViewInfo { /** 在select语句的第几个返回值 */ int index = -1; /** 在select语句的第几个返回值(多语言列) */ int langIndex = -1; /** VO对象对应的属性描述 */ PropertyDescriptor desc; /** 数组元素类型 */ Class<?> arrayElementType; /** 若属性是一个自对象,则children就是子对象属性的信息 */ Map<String, ViewInfo> children; ViewInfo getOrAddChild(PropertyDescriptor desc) { String name = desc.getName(); ViewInfo child = null; if (children == null) { children = new HashMap<>(16); } else { child = children.get(name); } if (child == null) { child = new ViewInfo(); child.desc = desc; children.put(name, child); } return child; } } }