急切地收集包含其他急切地得到的集合的实体

Posted

技术标签:

【中文标题】急切地收集包含其他急切地得到的集合的实体【英文标题】:Get eagerly collection of entities containing other eagerly got collections 【发布时间】:2011-12-16 12:45:22 【问题描述】:

我陷入了实体和字符串之间的 M:N 关系。一个用户可以拥有多个角色,并且每个角色可以分配给多个用户。角色只是一个字符串。角色包含在具有两列的表中:roleIdroleName

我创建了两个实体,但我绝对无法让它工作。第一个实体是用户:

@Entity
@Table(name="appUsers")
public class UserEntity 
    @Id
    private String login;
    private String password;
    @OneToMany(fetch=FetchType.EAGER,mappedBy="user") //we always need to load user's roles
    private Collection<UsersToRoles> roles;
    @Transient
    private Collection<String> roleNames;

    public String getLogin() 
        return login;
    

    public String getPassword() 
        return password;
    

    @PostLoad
    void prepareRoleNames() 
        roleNames = new HashSet<String>(roles.size());
        for (UsersToRoles mapping : roles)
            roleNames.add(mapping.getNameOfRole());
    

    public Collection<String> getRoles() 
        return roleNames;
    

第二个是与连接表关联的实体:

@Entity
@IdClass(UsersToRolesId.class)
public class UsersToRoles 
    @Id
    @SuppressWarnings("unused")
    @Column(name="login")
    private String login;
    @Id
    @SuppressWarnings("unused")
    @Column(name="roleId")
    private int roleId;
    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(name="userRoles", joinColumns=@JoinColumn(name="roleId"))
    private List<String> roleName;
    @ManyToOne
    @JoinColumn(name="login")
    @SuppressWarnings("unused")
    private UserEntity user;

    public String getNameOfRole() 
        if (roleName.isEmpty())
            throw new CommonError("Role name for roleId=" + roleId, AppErrors.ACCESSOR_UNAVAILABLE);
        return roleName.get(0);
    


class UsersToRolesId 
    private String login;
    private int roleId;

    /**
     * Implicit constructor is not public. We have to
     * declare public non-parametric constructor manually.
     */
    public UsersToRolesId() 
    

    @Override
    public int hashCode() 
        return 17*login.hashCode() + 37*roleId;
    

    @Override
    public boolean equals(Object obj) 
        if (!(obj instanceof UsersToRolesId))
            return false;
        UsersToRolesId ref = (UsersToRolesId)obj;
        return (this.login.equals(ref.login) && this.roleId == ref.roleId);
    

问题是,roleName 集合始终为空。我无法让它工作。当我在@CollectionTable 注释中的表名出错时,它仍然有效。 JPA 根本不获取子集合。它从与表UsersToRoles 联接的用户表中进行选择,但缺少与表userRoles 的联接。

我能做到吗?我是否可以急切地收集包含另一个急切获取的集合的实体?

【问题讨论】:

【参考方案1】:

您的映射完全错误。 UsersToRoles 有一个 roleId 列。因此它指的是一个单一的角色。它怎么会有角色名称的集合?登录列在实体中映射了两次。此外,在我看来,这就像一个简单的连接表,除了 roleId 和 login 之外没有任何其他属性,它们分别是 User 和 Role 的 ID 的外键。

您应该有两个实体:UserRole,使用 UsersToRoles 表作为连接表的 ManyToMany 关联。而已。 UsersToRoles 表不应映射为实体:它是一个纯连接表。

【讨论】:

【参考方案2】:

JPA 提供者通常有一个配置属性表示默认的急切获取深度,即 hibernate.max_fetch_depth 用于 Hibernate。检查增加时是否可以看到更多。

另外,想想你的设计。仅在有限的情况下(性能方面)急切地获取集合的子集合可能是一个好主意。当您像这样注释您的实体时,您将在所有用例中使用急切获取。也许您最好使用“懒惰”并仅通过带有 JOIN FETCH 子句的查询显式地急切地获取它?

【讨论】:

以上是关于急切地收集包含其他急切地得到的集合的实体的主要内容,如果未能解决你的问题,请参考以下文章

延迟加载孩子,里面有急切的集合

实体框架急切加载不返回数据,延迟加载有

Hibernate:为啥 FetchType.LAZY 注释的集合属性急切地加载?

在 EFCore 急切加载 LINQ 查询中,如何在 ThenInclude() 表达式中引用***实体?

在 NHibernate 中强制进行急切的选择

Fluent NHibernate:在映射中急切加载多个集合