Hibernate(4.1.2)和Spring(3.1.2) - ManyToMany关系不存储JoinTable中的记录

时间:2013-03-15 17:23:56

标签: spring hibernate jpa

我遇到了问题,需要您的帮助才能解决这个问题。希望这一点可能成为类似问题的参考......

在我最小化的商业模式中,有用户和标题。应首先创建标题,并将其分配给许多用户,并且用户可以共享相同的标题。因此,我使用@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

以下是我对每种方法的看法:

Approach1

根据Hibernate文档(http://docs.jboss.org/hibernate/core/4.1/manual/en-US/html/ch07.html#d5e5537),我应该遵循Approach1。特别是,因为文档明确提到:

  

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

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

Approach2

它有效,但它忽略了我的@JoinTable定义并创建了自己的表:tb_user_tb_title。它闻起来很腥。

Approach3

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

1 个答案:

答案 0 :(得分:4)

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

@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);

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

我真的希望这个问题成为类似问题的参考,但我对此表示怀疑,因为我已经回答了这个问题了很多次,并且它一再出现,尽管文档中已经清楚地解释了这一点:

  

如果关联是双向的,则一方必须是所有者,一方必须是反向结束(即,在更新关联表中的关系值时将忽略它):

     

[在双向多对多关联的每一侧都有适当的注释示例]