JPA-Test/src/main/java/com/guwan/backend/builder/JoinBuilder.java

336 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.guwan.backend.builder;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.*;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.stream.Collectors;
/**
* 自定义连表查询构建器
* 用于构建复杂的多表连接查询,支持指定返回字段和条件
*/
public class JoinBuilder<T> {
private final Class<T> rootClass;
private final Map<Class<?>, JoinInfo> joins = new HashMap<>();
private final Map<Class<?>, List<Condition>> conditions = new HashMap<>();
private Map<String, Object> namedSelections = new LinkedHashMap<>();
private JoinBuilder(Class<T> rootClass) {
this.rootClass = rootClass;
}
public static <T> JoinBuilder<T> from(Class<T> rootClass) {
return new JoinBuilder<>(rootClass);
}
/**
* 添加连接表
*
* @param joinClass 要连接的表实体类
* @param sourceField 主表的外键字段
* @param targetField 连接表的目标字段
*/
public void join(Class<?> joinClass, String sourceField, String targetField) {
joins.put(joinClass, new JoinInfo(sourceField, targetField));
}
/**
* 添加等值条件
*
* @param entityClass 实体类
* @param fieldName 字段名
* @param value 字段值
*/
public <V> void equal(Class<?> entityClass, String fieldName, V value) {
if (!conditions.containsKey(entityClass)) {
conditions.put(entityClass, new ArrayList<>());
}
conditions.get(entityClass).add(new Condition(fieldName, value, Operator.EQUAL));
}
/**
* 添加一个具有别名的字段选择(字符串版本)
*
* @param alias 字段别名
* @param entityClass 实体类
* @param fieldName 字段名称
*/
public <E> void selectAs(String alias, Class<E> entityClass, String fieldName) {
namedSelections.put(alias, new SelectionInfo(entityClass, fieldName));
}
/**
* 执行查询并返回结果
* @param entityManager EntityManager实例
* @return 查询结果列表
*/
public List<Object[]> execute(EntityManager entityManager) {
// 创建CriteriaBuilder
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
// 创建CriteriaQuery
CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder.createQuery(Object[].class);
// 设置根实体
Root<T> root = criteriaQuery.from(rootClass);
// 创建连接关系
Map<Class<?>, From<?, ?>> fromMap = createJoins(root, criteriaQuery);
// 应用所有条件,包括表之间的连接条件和额外查询条件
List<Predicate> predicates = applyAllConditions(root, fromMap, criteriaBuilder);
// 添加条件到查询
if (!predicates.isEmpty()) {
criteriaQuery.where(criteriaBuilder.and(predicates.toArray(new Predicate[0])));
}
// 设置查询结果投影
configureSelections(root, fromMap, criteriaQuery);
// 执行查询并返回结果
return entityManager.createQuery(criteriaQuery).getResultList();
}
public long count(EntityManager entityManager) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
Root<T> root = countQuery.from(rootClass);
// 创建连接关系
Map<Class<?>, From<?, ?>> fromMap = createJoins(root, countQuery);
// 应用条件,包括表之间的连接条件和额外查询条件
List<Predicate> predicates = applyAllConditions(root, fromMap, criteriaBuilder);
// 添加条件到查询
if (!predicates.isEmpty()) {
countQuery.where(criteriaBuilder.and(predicates.toArray(new Predicate[0])));
}
// 设置count查询
countQuery.select(criteriaBuilder.count(root));
// 执行查询并返回结果
return entityManager.createQuery(countQuery).getSingleResult();
}
public <R> List<R> executeAndMapWithPaging(EntityManager entityManager,
Class<R> dtoClass,
int page,
int size) {
List<Object[]> results = executeWithPaging(entityManager, page, size);
return mapToDto(results, dtoClass);
}
public List<Object[]> executeWithPaging(EntityManager entityManager, int page, int size) {
// 创建CriteriaBuilder
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
// 创建CriteriaQuery
CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder.createQuery(Object[].class);
// 设置根实体
Root<T> root = criteriaQuery.from(rootClass);
// 创建连接关系
Map<Class<?>, From<?, ?>> fromMap = createJoins(root, criteriaQuery);
// 应用所有条件
List<Predicate> predicates = applyAllConditions(root, fromMap, criteriaBuilder);
// 添加条件到查询
if (!predicates.isEmpty()) {
criteriaQuery.where(criteriaBuilder.and(predicates.toArray(new Predicate[0])));
}
// 设置查询结果投影
configureSelections(root, fromMap, criteriaQuery);
// 创建查询并设置分页
jakarta.persistence.Query query = entityManager.createQuery(criteriaQuery);
query.setFirstResult(page * size);
query.setMaxResults(size);
// 执行查询并返回结果
return query.getResultList();
}
/**
* 创建连接 - 修改为使用from而不是join
*/
private Map<Class<?>, From<?, ?>> createJoins(Root<T> root, CriteriaQuery<?> query) {
Map<Class<?>, From<?, ?>> fromMap = new HashMap<>();
fromMap.put(rootClass, root);
// 对于每个需要连接的表创建一个单独的From
for (Map.Entry<Class<?>, JoinInfo> entry : joins.entrySet()) {
Class<?> joinClass = entry.getKey();
From<?, ?> joinRoot = query.from(joinClass);
fromMap.put(joinClass, joinRoot);
}
return fromMap;
}
/**
* 应用所有条件,包括表之间的连接条件和额外查询条件
*/
private List<Predicate> applyAllConditions(Root<T> root, Map<Class<?>, From<?, ?>> fromMap, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
// 首先添加表连接条件
for (Map.Entry<Class<?>, JoinInfo> entry : joins.entrySet()) {
Class<?> joinClass = entry.getKey();
JoinInfo joinInfo = entry.getValue();
if (fromMap.containsKey(joinClass)) {
From<?, ?> joinFrom = fromMap.get(joinClass);
Predicate joinCondition = criteriaBuilder.equal(
root.get(joinInfo.sourceField),
joinFrom.get(joinInfo.targetField)
);
predicates.add(joinCondition);
}
}
// 然后添加普通查询条件
for (Class<?> entityClass : conditions.keySet()) {
for (Condition condition : conditions.get(entityClass)) {
if (fromMap.containsKey(entityClass)) {
From<?, ?> from = fromMap.get(entityClass);
predicates.add(applyCondition(condition, from, criteriaBuilder));
}
}
}
return predicates;
}
/**
* 将查询结果映射到DTO对象
* @param results 查询结果
* @param dtoClass DTO类
* @param <R> DTO类型
* @return DTO对象列表
*/
public <R> List<R> mapToDto(List<Object[]> results, Class<R> dtoClass) {
try {
// 获取所有字段名(顺序与查询结果一致)
List<String> fieldNames = new ArrayList<>(namedSelections.keySet());
// 获取构造函数
Class<?>[] paramTypes = new Class[fieldNames.size()];
Arrays.fill(paramTypes, Object.class);
Constructor<R> constructor = dtoClass.getDeclaredConstructor(paramTypes);
// 映射结果
return results.stream()
.map(row -> {
try {
return constructor.newInstance(row);
} catch (Exception e) {
throw new RuntimeException("Failed to map result to DTO: " + e.getMessage(), e);
}
})
.collect(Collectors.toList());
} catch (Exception e) {
throw new RuntimeException("Failed to create DTO mapper: " + e.getMessage(), e);
}
}
/**
* 配置查询结果投影
*/
private void configureSelections(Root<T> root, Map<Class<?>, From<?, ?>> fromMap, CriteriaQuery<?> query) {
if (!namedSelections.isEmpty()) {
// 处理命名选择
List<Selection<?>> selections = new ArrayList<>();
for (Map.Entry<String, Object> entry : namedSelections.entrySet()) {
String alias = entry.getKey();
Object value = entry.getValue();
if (value instanceof SelectionInfo) {
SelectionInfo info = (SelectionInfo) value;
Path<?> path;
if (fromMap.containsKey(info.entityClass)) {
path = fromMap.get(info.entityClass).get(info.fieldName);
selections.add(path.alias(alias));
}
}
}
if (!selections.isEmpty()) {
query.multiselect(selections);
return;
}
}
}
private <X> Predicate applyCondition(Condition condition, From<?, X> from, CriteriaBuilder criteriaBuilder) {
switch (condition.operator) {
case EQUAL:
return criteriaBuilder.equal(from.get(condition.fieldName), condition.value);
// 可以添加更多操作符支持如LIKE, IN, GREATER_THAN等
default:
throw new UnsupportedOperationException("Unsupported operator: " + condition.operator);
}
}
/**
* 连接信息
*/
private static class JoinInfo {
private final String sourceField;
private final String targetField;
JoinInfo(String sourceField, String targetField) {
this.sourceField = sourceField;
this.targetField = targetField;
}
}
/**
* 选择字段信息
*/
private static class SelectionInfo {
private final Class<?> entityClass;
private final String fieldName;
SelectionInfo(Class<?> entityClass, String fieldName) {
this.entityClass = entityClass;
this.fieldName = fieldName;
}
}
private enum Operator {
EQUAL
// 可以添加更多操作符支持如LIKE, IN, GREATER_THAN等
}
private static class Condition {
private final String fieldName;
private final Object value;
private final Operator operator;
Condition(String fieldName, Object value, Operator operator) {
this.fieldName = fieldName;
this.value = value;
this.operator = operator;
}
}
}