我有问题,需要您的帮助来解决此问题。希望这一步可能成为类似问题的参考。
在我最小化的业务模型中,有用户和标题。应该首先创建标题,并且可以将其分配给许多用户,并且用户可以共享相同的标题。因此,我创建了两个具有@ManyToMany关系的名为User和Title的实体,并确定Title应该拥有该关系。另外,我有一个UnitTest来运行此示例。
用户实体
public class User { Long id; String name; Set<Title> titles = new HashSet<Title>(); @Id @GeneratedValue @Column(name = "id") public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } /*============ Approach1 ============*/ // @ManyToMany(mappedBy = "users") /*============ Approach2 ============*/ // @ManyToMany /*============ Approach3 ============*/ @ManyToMany @JoinTable( name = "tb_title_user", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "title_id")) public Set<Title> getTitles() { return titles; } public void setTitles(Set<Title> titles) { this.titles = titles; } }
标题实体
public class Title { Long id; String description; Set<User> users = new HashSet<User>(); @Id @GeneratedValue @Column(name = "id") public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Column(name = "description") public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } /*============ Approach1 & Approach2 & Approach3 ============*/ @ManyToMany @JoinTable( name = "tb_title_user", joinColumns = @JoinColumn(name = "title_id"), inverseJoinColumns = @JoinColumn(name = "user_id")) public Set<User> getUsers() { return users; } public void setUsers(Set<User> users) { this.users = users; } }
单元测试
public class UserTest { @Autowired private SessionFactory sessionFactory; @Test @Rollback(false) @Transactional public void saveUser(){ Session session = sessionFactory.getCurrentSession(); String now = new Date().toString(); Title title = new Title(); title.setDescription("TitleDescription: " + now); session.save(title); User user = new User(); user.setName("UserName: " + now); user.getTitles().add(title); session.saveOrUpdate(user); } }
如果看上面的代码,您将看到三种不同的方法。下面说明数据是否正确存储在数据库表中:
Title User JoinTable Approach1 Yes Yes No Approach2 Yes Yes Yes Approach3 Yes Yes Yes
这是我对每种方法的想法:
方法1
根据Hibernate文档(http://docs.jboss.org/hibernate/core/4.1/manual/en- US/html/ch07.html#d5e5537),我应该遵循Approach1。特别地,因为文档明确提到:
“如前所述,另一边不必(不必)描述物理映射:包含所有者方属性名称的简单mapedBy参数将两者绑定。”
如果我理解正确,则不必(不必)在User实体中添加@JoinTable。
方法2
它可以工作,但是会忽略我的@JoinTable定义并创建自己的表::tb_user_tb_title。对我来说有点腥。
tb_user_tb_title
方法3
它可以工作,但是文档说不要使用它。因此,在我看来,我可能会后悔在企业产品中使用这种方法。
唯一正确的方法是第一种:
@ManyToMany(mappedBy = "users") public Set<Title> getTitles() { return titles; } ... @ManyToMany @JoinTable(name = "tb_title_user", joinColumns = @JoinColumn(name = "title_id"), inverseJoinColumns = @JoinColumn(name = "user_id")) public Set<User> getUsers() { return users; }
反面使用该mappedBy属性说:“我是反面。请查看users目标实体中的属性,以查看此关联如何映射。”
mappedBy
users
您在做错的是,您只能在测试中修改反面。JPA / Hibernate仅考虑所有者方是否知道关联。所以不要做
user.getTitles().add(title);
你应该做
title.getUsers().add(user);
甚至两者都做,以确保对象图是连贯的。
我真的希望这个方法可以作为类似问题的参考,但是我对此表示怀疑,因为我已经回答了无数次此问题,并且它一遍又一遍地出现,尽管在文档中已明确说明:
如果关联是双向的,则一侧必须是所有者,而一侧必须是反向端(即,在更新关联表中的关系值时,它将被忽略): [遵循一个示例,该示例在双向多对多关联的每一侧都带有适当的注释]
如果关联是双向的,则一侧必须是所有者,而一侧必须是反向端(即,在更新关联表中的关系值时,它将被忽略):
[遵循一个示例,该示例在双向多对多关联的每一侧都带有适当的注释]