首页 > 解决方案 > 从表和连接表中选择部分列 Criteria API

问题描述

我正在尝试从不同的表中选择 2 列(它们已连接),但我无法使用 Criteria API 使其工作。问题是,我创建了一个 DTO 类作为 Projection,但我无法将连接的表实体转换为内部类。为了澄清,这是我的课程:

用户:

    @Entity
    @Table(name = "users", indexes = {@Index(columnList = "email", unique = true)})
    public class User extends BaseEntity {

        @Column(nullable = false)
        private String firstName;

        @ManyToMany
        @JoinTable(name = "user_roles",
                joinColumns = @JoinColumn(name = "user_id"),
                inverseJoinColumns = @JoinColumn(name = "role_id"))
        private Set<Role> roles;

角色:

@Table(name = "roles")
@Entity
public class Role extends BaseEntity {

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String description;

DTO 类:

public class UserRoleDto {
    private String email;
    private Set<RoleDto> roles;

    public UserRoleDto(String email, Set<RoleDto> roles) {
        this.email = email;
        this.roles = roles;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Set<RoleDto> getRoles() {
        return roles;
    }

    public void setRoles(Set<RoleDto> roles) {
        this.roles = roles;
    }

    public static class RoleDto {
        private String name;

        public RoleDto(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

最后,我尝试过的:

    @Override
    public Optional<UserRoleDto> findByIdWithRoles(UUID id) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<UserRoleDto> query = builder.createQuery(UserRoleDto.class);

        Root<User> root = query.from(User.class);
        root.fetch("roles", JoinType.LEFT);

        query.where(builder.equal(root.get("id"), id));
        query.select(builder.construct(UserRoleDto.class, root.get("email"), root.join("roles")));

        UserRoleDto user = entityManager.createQuery(query).getSingleResult();

        return Optional.ofNullable(user);
    }

如您所见,我只想查询 user.firstName 并从 Role 连接表中查询名称。如何在这里从 Role 转换为 UserRoleDto.RoleDto?

谢谢!

标签: javaspringhibernatejpacriteria-api

解决方案


这行不通。您只能使用 JPA 构造函数语法从标量结果创建对象,但不能用于嵌套结构。您必须自己构建对象图。

无论如何,这是Blaze-Persistence Entity Views的完美用例。

Blaze-Persitence 是一个基于 JPA 的查询构建器,它支持 JPA 模型之上的许多高级 DBMS 功能。我在它之上创建了实体视图,以允许在 JPA 模型和自定义接口定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您以您喜欢的方式定义目标结构,并通过 JPQL 表达式将属性(getter)映射到实体模型。由于属性名称用作默认映射,因此您通常不需要显式映射,因为 80% 的用例是拥有作为实体模型子集的 DTO。

您的模型的映射可能看起来很简单,如下所示

@EntityView(User.class)
interface UserRoleDto {
    String getEmail();
    Set<RoleDto> getRoles();
}

@EntityView(Role.class)
interface RoleDto {
    String getName();
}

查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

UserRoleDto dto = entityViewManager.find(entityManager, UserRoleDto.class, id);

但是 Spring Data 集成允许您几乎像 Spring Data Projections 一样使用它:https ://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

它只会获取您告诉它获取的映射


推荐阅读