当前位置: 首页 > 面试题库 >

Hibernate多对多关联:左侧集合包含元素,但右侧集合为空

南宫奇思
2023-03-14
问题内容

我在持久层中存在多对多关联的问题。我的情况如下:

一个用户可以具有多个角色,并且一个角色可以具有与其连接的多个用户。在测试期间,我遇到了一个奇怪的行为。我创建了角色对象和几个用户对象。该角色已设置给每个用户。此后,使用DAO保存了用户。然后,加载一个用户,以检查他是否在保存用户对象之前获得了传递给他的角色。呼叫getRoles()用户表明角色设置正确。

要检查反方向是否也起作用,可以使用角色DAO从数据库中加载角色对象。但是,调用getUsers()角色对象只会返回一个空集,尽管它应包含具有该角色的所有用户。

我仔细检查了数据库表,但是一切似乎都没问题。用户,角色和user_role表均已正确填写。

那么,为什么角色对象不包含任何用户?

我在以下类中使用Hibernate和Spring。

用户类别

@Entity
@Table
public class User extends BusinessObject {

    ...

    // Option 1
    @ManyToMany(fetch = FetchType.LAZY,
                cascade = CascadeType.ALL,
                targetEntity=Role.class)
    @JoinTable(name= "user_role",
               joinColumns = {@JoinColumn(name="user_id")},
               inverseJoinColumns = {@JoinColumn(name="role_id")})

    // Option 2
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name= "user_role", 
                   joinColumns = {@JoinColumn(name="user_id")},
           inverseJoinColumns = {@JoinColumn(name="role_id")})
    private Set<Role> roles = new HashSet<Role>();

    ... 
}

角色类

@Entity
@Table
public class Role extends BusinessObject {
    ...

    // Option 1
    @ManyToMany(fetch = FetchType.LAZY, 
                cascade = CascadeType.ALL,
                mappedBy= "roles",
                targetEntity = User.class)

    // Option 2
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name= "user_role", 
                   joinColumns = {@JoinColumn(name="role_id")},
                   inverseJoinColumns = {@JoinColumn(name="user_id")})
    private Set<User> users = new HashSet<User>();

    ... 
}

为了测试,我在JUnit测试类中使用以下代码。

@Test
public void test(){     
    Transaction trans = sessionFactory.getCurrentSession().beginTransaction();

    Role userAdminRole = new Role();
    userAdminRole.setName(RoleName.USER_ADMIN);
    Role userRole = new Role();
    userRole.setName(RoleName.USER);

    User user1 = new User();
    user1.setEmail("user1@user.de");        
    user1.getRoles().add(userAdminRole);
    user1.getRoles().add(userRole);
    userDao.save(user1);

    User user2 = new User();
    user2.setEmail("user2@user.de");
    user2.getRoles().add(role);
    userDao.save(user2);

    User user3 = new User();
    user3.setEmail("user3@user.de");
    user3.getRoles().add(role);
    userDao.save(user3);

    trans.commit();

    User loadedUser = userDao.load(user1.getId());

            // Tests passes
    Assert.assertNotNull(loadedUser);
    Assert.assertEquals(user1, loadedUser);

    Set<Role> roles = loadedUser.getRoles();

            // Tests passes
    Assert.assertEquals(2, roles.size());

    Role loadedUserAdminRole = roleDao.findByName(RoleName.USER_ADMIN);
    Set<User> users = loadedUserAdminRole.getUsers();

    // Test fails: Count is 0 instead of 3 !!!!!!!
    Assert.assertEquals(3, users.size());
}

更新

对不起,我忘了提到一件事。当我测试代码时,我当然没有在每个类文件中两次标记多对多关联。相反,我在每个类文件中使用了选项1或选项2。


问题答案:

问题可能是由于您两次映射了相同的双向关联。如果您对相同的联接表或联接列两次告诉Hibernate,则存在问题。在双向关联中,关联的一端必须映射该关联,而另一端必须使用mappedBy属性告诉Hibernate这是另一端的逆。

由于多对多是完全对称的,因此请选择一端作为所有者(即,映射关联并因此具有@JoinTable注释的一端)。另一侧只是反面,因此没有@JoinTable注释,但具有mappedBy属性。

例:

@Entity
@Table
public class User extends BusinessObject {

    ...

    // This end is the owner of the association
    @ManyToMany
    @JoinTable(name= "user_role",
               joinColumns = {@JoinColumn(name="user_id")},
               inverseJoinColumns = {@JoinColumn(name="role_id")})  
    private Set<Role> roles = new HashSet<Role>();      
    ... 
}

@Entity
@Table
public class Role extends BusinessObject {
    ...

    // This end is not the owner. It's the inverse of the User.roles association
    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<User>();

    ... 
}

补充笔记:

  • targetEntity没什么用,因为Hibernate知道了它的得益于的通用类型Set。如果Set是一个Set<SomeInterface>
  • CascadeType.ALL当然不是您想要的。删除用户时是否要删除用户的所有角色?具有这些角色的其他用户应该怎么办?
  • 您几乎应该总是初始化双向关联的两端。Hibernate将所有者端(即没有mappedBy属性的端)考虑在内以保持关联。
  • 所有这些都在Hibernate参考文档中进行了说明。阅读:它充满了有用的信息,并不难理解。


 类似资料:
  • 假设我有这些类,请注意实体无论如何都不相关。 假定我有一个类,它的属性为,我可以在其中存储一个笔记本ID列表或任何字符串ID。假设有idsPointingToanEntity=[1,2,3],那么这个[1,2,3]在数据库中有一个等效的膝上型计算机条目。 注意:也可以是另一个实体,使不是一个选项

  • 问题内容: 我想更好地了解之间的区别 (1)传统的 多值关系/关联 和 (2) 可嵌入(和基本)类型 的JPA2 集合 我看到了语法上的差异,但想知道是否还会 影响性能 。在后台,数据库实现看起来非常相似。 凭直觉,我通常会使用for 组成方案 。但是即使那样感觉也很相似。 我在这里错过了本质吗?在某些方面,一个比另一个更有效吗? 谢谢你J 问题答案: 凭直觉,我通常将@ElementCollec

  • 我有两个类和,它们具有一对多关系。 假设我已经在我的数据库结构中持久化了,如下所示: ---编辑--- 实体不是什么花哨的东西,比如:

  • https://www.geeksforgeeks.org/count-of-larger-elements-on-right-side-of-each-element-in-an-array/#:~: text=朴素的做法:最简单的做法,一边然后打印出来。 我试图以大于左边数字的顺序计算数字,就像上面网站上描述的那样,但网站中的输出是arraylist,但我只想要一个数字,它是arraylist

  • 问题内容: 我一直遇到这个问题: 我想让hibernate管理一个表示集合集合的表。例如: 地图地图 套装清单 清单地图 例如,我希望能够代表这一点: 作为一个表: 没有自定义的hibernate代码似乎是不可能的,我不介意。但是我希望有人对自定义代码的外观有所指导。 我应该扩展AbstractPersistentCollection吗? CompositeUserType? 可以管理多个表是否正