【问题标题】:Hibernate (4.1.2) and Spring (3.1.2) – ManyToMany relationship does not store records in JoinTableHibernate (4.1.2) 和 Spring (3.1.2) – ManyToMany 关系不在 JoinTable 中存储记录
【发布时间】:2013-03-15 17:23:55
【问题描述】:

我有一个问题,需要您的帮助来解决这个问题。希望这个胎面可以成为类似问题的参考……

在我最小化的业务模型中,有用户和标题。 Title 应该先创建,并且可以分配给多个用户,并且用户可以共享相同的 Title。因此,我创建了两个名为 User 和 Title 的实体,它们具有 @ManyToMany 关系,并决定 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。特别是,因为文档明确提到:

“如前所述,对方不必(不得)描述 物理映射:包含所有者的简单 mappedBy 参数 侧属性名称绑定两者。”

如果我理解正确,我不必(不得)在 User 实体中添加 @JoinTable。

方法2

它可以工作,但它忽略了我的@JoinTable 定义并创建了自己的名为:tb_user_tb_title 的表。我闻起来很腥。

方法3

它可以工作,但文档说不要使用它。因此,在我看来,我可能会后悔在企业产品中使用这种方法。

【问题讨论】:

  • 感觉你的关系错了,根据你上面的描述[Titles应该先创建,可以分配给多个User,Users可以共享同一个Title],应该是oneToMany吧? !!
  • 嗨,Ahmed,这实际上是一种多对多关系“并且用户可能共享相同的标题”。此外,在此示例中,我不需要任何级联。我会说“首先应该创建标题”与我的业务工作流程相关,并且可以被视为业务规则。因此,我确实看到了任何定义差距。感谢您的提问。
  • 有什么问题?正如您所发现的,文档说您必须使用 mappedBy,并且不得在反面指定映射。
  • 您好 JB Nizet,问题是:如果我想指定我的 JoinTable,正确的方法是什么?我期待看到不同的方法(Approach4)。如果我使用 Approach2,Hibernate 会创建一个我无法控制的 JoinTable……如果还不清楚,请告诉我。

标签: spring hibernate jpa


【解决方案1】:

唯一正确的方法是第一种:

@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属性,看看这个关联是如何映射的。”

您做错的是您只修改了测试中的反面。 JPA/Hibernate 只考虑所有者方知道是否存在关联。所以不要这样做

user.getTitles().add(title);

你应该这样做

title.getUsers().add(user);

或者更好的是,两者都做,以确保对象图是连贯的。

我真的希望这个步骤可以成为类似问题的参考,但我对此表示怀疑,因为我已经回答了这个问题无数次,而且它不断地出现,尽管它在文档中得到了明确的解释:

如果关联是双向的,一侧必须是所有者,一侧必须是反向端(即更新关联表中的关系值时将被忽略):

[下面是一个示例,在双向多对名关联的每一侧都有适当的注释]

【讨论】:

  • 太棒了!非常感谢 JB Nizet 的详细解释。它工作得很好,在这个例子中,在我的真实案例中(我使用这个例子只是为了在最小化的场景中重现这个问题)。希望如果这个问题再次出现,我也可以帮助其他人。干杯。
  • 它对我也不起作用,直到我将级联放入用户中:@ManyToMany(cascade=CascadeType.ALL)
猜你喜欢
  • 1970-01-01
  • 2014-08-22
  • 1970-01-01
  • 2012-06-03
  • 2012-07-13
  • 1970-01-01
  • 1970-01-01
  • 2017-10-16
  • 2015-05-14
相关资源
最近更新 更多