有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java QueryDsl投影元素集合

我试图弄清楚如何使用枚举列表(@ElementCollection)对实体进行DTO投影。不幸的是,缺少QueryDsl文档,在这里我只发现版本3的结果不适用于版本4

@Entity
public class User {
    private String username;

    @Enumerated(EnumType.STRING)
    @ElementCollection(targetClass = Permission.class)
    private Set<Permission> permissions;
}

我想要一个DTO,它具有权限枚举或简单字符串的集合/列表/数组(无论如何都将转换为JSON)。简单的构造函数表达式不起作用:

List<UserDto> users = new JPAQueryFactory(eM).select(
            Projections.constructor(UserDto.class,
                    QUser.user.username, QUser.user.permissions))
            .from(QUser.user)
            .fetch();

给我org.hibernate.QueryException: not an entity

我看到的所有带有.transform()的示例都使用groupBy并返回一个映射。我正在动态地生成这些查询,我想要一个DTO列表,而不是DTO列表和映射

编辑:

如果我要编写一个本机PostgreSQL查询:

select id, username, array_remove(array_agg(up.permissions), null) as permissions
from users u
left join users_permissions up on up.uid = u.id
group by u.id;

编辑2:

我想这就是我对JPQL的看法呕吐:

List<UserDto> users = (List<UserDto>) eM.getEntityManager().createQuery(
                "SELECT u.id, u.username, u.tenantId, u.fullname, u.active, u.localeKey, perms " +
                        "FROM User u " +
                        "LEFT JOIN u.permissions perms")
                .unwrap(org.hibernate.query.Query.class)
                .setResultTransformer(
                        new ResultTransformer() {
                            private Map<Long, UserDto> res = new HashMap<>();

                            @Override
                            public Object transformTuple(Object[] tuple, String[] aliases) {
                                UserDto u = res.get(tuple[0]);
                                if (u == null) {
                                    u = new UserDto((Long) tuple[0], (String) tuple[1], "", (String) tuple[2], (String) tuple[3], (boolean) tuple[4], (String) tuple[5], EnumSet.of((Permission) tuple[6]));
                                    res.put(u.getId(), u);
                                } else {
                                    u.getPermissions().add((Permission) tuple[6]);
                                }

                                return null;
                            }

                            @Override
                            public List<UserDto> transformList(List tuples) {
                                return new ArrayList<>(res.values());
                            }
                        })
                .getResultList();

共 (2) 个答案

  1. # 1 楼答案

    下面是使用.transform()方法的完整示例

    import com.querydsl.core.ResultTransformer;
    import com.querydsl.core.Tuple;
    import com.querydsl.core.group.GroupBy;
    import com.querydsl.core.types.Expression;
    import com.querydsl.core.types.OrderSpecifier;
    import com.querydsl.core.types.Predicate;
    import com.querydsl.core.types.Projections;
    import com.querydsl.jpa.impl.JPAQuery;
    import lombok.AccessLevel;
    import lombok.RequiredArgsConstructor;
    import lombok.experimental.FieldDefaults;
    import org.springframework.data.domain.Pageable;
    import org.springframework.stereotype.Repository;
    
    import java.util.List;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    @Repository
    @FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
    @RequiredArgsConstructor
    public class MyDaoImpl implements MyDao {
    
      @PersistenceContext
      EntityManager entityManager;
    
      @Override
      public List<Dto> findAll(Pageable pageable, Predicate predicate, OrderSpecifier<?>[] sorting) {
        return buildQuery()
            .where(predicate)
            .limit(pageable.getPageSize())
            .offset(pageable.getOffset())
            .orderBy(sorting)
            .transform(buildDtoTransformer());
      }
    
      private JPAQuery<Tuple> buildQuery() {
        return new JPAQuery<>(entityManager)
            .from(QEntity.entity)
            .select(buildSelectExpressions());
      }
    
      private ResultTransformer<List<Dto>> buildDtoTransformer() {
        return GroupBy
            .groupBy(QEntity.entity.id)
            .list(Projections.constructor(Dto.class, buildSelectExpressions()));
      }
    
      private Expression<?>[] buildSelectExpressions() {
        return new Expression[] {
            QEntity.entity.id,
            QEntity.entity.mnemonic,
            QEntity.entity.label,
            QEntity.entity.lastUpdateDate,
            QEntity.entity.status,
            QEntity.entity.updatedBy,
        };
      }
    }
    

    专业人士

    • 对于.select().transform()方法,使用相同的表达式数组。这意味着表达可以在一个地方

    缺点

    • 没有分组。这意味着没有嵌套的集合

    • 表达式顺序和DTO构造函数参数顺序应该相同

      new Expression[] {
         QEntity.entity.id,
         QEntity.entity.mnemonic,
         QEntity.entity.label,
         QEntity.entity.lastUpdateDate,
         QEntity.entity.status,
         QEntity.entity.updatedBy,
      };
      
      public class Dto {
      
         public Dto(Integer id,
                   String mnemonic,
                   String label,
                   LocalDateTime lastUpdateDate,
                   Boolean status,
                   String updatedBy) {
          this.id = id;
          this.mnemonic = mnemonic;
          this.label = label;
          this.lastUpdateDate = lastUpdateDate;
          this.status = status;
          this.updatedBy = updatedBy;
        }
      }
      
    • 创建嵌套对象逻辑应该在DTO构造函数中

    结论

    可以对单级简单对象使用.transform()

    如果您需要额外的操作(作为构建分层响应结构的分组),您应该创建自己的mapper类。映射器类将接收Collection<Tuple>并返回List<YourDto>Set<YourDto>

  2. # 2 楼答案

    好吧,我终于明白了。在这种情况下,实际上必须使用transformer,这是有道理的,因为您想要聚合几行

    我得把它挖出来。如果你不使用IDE,静态导入实际上会让事情变得棘手,但是像我一样在Github上阅读它。我几乎找到了解决方案,但我使用了Expressions.set(),而不是GroupBy.set()

    EnumPath<Permission> perm = Expressions.enumPath(Permission.class, "perm");
    
    List<UserDto> users = new JPAQueryFactory(eM.getEntityManager())
                    .selectFrom(QUser.user)
                    .leftJoin(QUser.user.permissions, perm)
                    .transform(
                            groupBy(QUser.user.id)
                            .list(Projections.constructor(UserDto.class,
                                    QUser.user.id, QUser.user.username, Expressions.stringTemplate("''"), QUser.user.tenantId,
                                    QUser.user.fullname, QUser.user.active, QUser.user.localeKey, GroupBy.set(perm))));
    

    这在外观上比JPQL/Hibernate ResultTransformer版本好得多