hibernate5(14)注解映射[6]多对多中间表关联

简介: <div class="markdown_views"><p>在我们的角色管理系统中,一个用户可以有多种角色,一种角色可以赋予多个用户,显然用户和角色就是典型的多对多关系。又或者博客网站上,用户与文章点赞记录也是一个多对多关系,即一个用户可以点赞多篇文章,一篇文章可以给多个用户点赞等,这时候,我们往往需要附加一些信息,比如授权时间、点赞时间等。在上面两个实例中,都可对应于hi

在我们的角色管理系统中,一个用户可以有多种角色,一种角色可以赋予多个用户,显然用户和角色就是典型的多对多关系。又或者博客网站上,用户与文章点赞记录也是一个多对多关系,即一个用户可以点赞多篇文章,一篇文章可以给多个用户点赞等,这时候,我们往往需要附加一些信息,比如授权时间、点赞时间等。在上面两个实例中,都可对应于hibernate多对多映射关系的两种方式,在多对多映射中,我们往往使用中间表来建立关联关系,而且会是双向关联,确保任意一方添加或删除,都可以对中间表进行操作来维护关联关系。

下面我们来看多对多映射的第一种实现:
我们先看一个错误的配置:

/****************用户类***************/
@Entity
@Table(name = "t_user3")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String userName;

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Role> roles;

    //忽略get 和set方法
}
/****************角色类***************/
@Entity
@Table(name = "t_role3")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String roleName;
    @ManyToMany(cascade = CascadeType.ALL)
    private Set<User> users;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result
                + ((roleName == null) ? 0 : roleName.hashCode());
        return result;
    }
     //忽略get 和set方法
}

在这里我们简单的通过注解ManyToMany建立了两边关联关系,并且级联所有操作,这时候,调用我们的测试方法:

@Test
public void test1(){
    User user = new User();
    user.setUserName("userName");
    Role role = new Role();
    role.setRoleName("roleName");
    Set<Role> roles = new HashSet<Role>();
    roles.add(role);
    user.setRoles(roles);//建立关联关系
    session.save(user);
}

执行方法,我们会看到数据记录如下图
这里写图片描述
在数据库中,hibernate帮我们生成了4张表,其中2张是中间表,是因为User和Role都是关联关系主控方,会以自己为主建立一张中间表,通过级联插入操作我们会发现,我们添加User,即使级联添加了Role,但维护关系在由User主控的中间表t_user3_t_role3中,这样,如果我尝试从Role方对这条记录进行级联操作,因为在Role放的主控表t_role3_t_user3中找不到维护关系,则会导致级联操作失败!
比如我们执行如下操作:

Role role = session.get(Role.class, 2);//获取刚刚插入的记录
System.out.println(role.getUsers().size());//结果打印0,即从Role端找不到关联的User
session.delete(role);//尝试删除操作,看能否级联删除

这是查看记录
这里写图片描述
发现确实只有Role表的记录被删除了。当然,如果数据库存在外键关联的话,这个删除操作是会失败的,因为中间表t_user3_t_role3的记录roles_id=2会进行约束

接下来,我们先恢复role端的记录:
这里写图片描述
再执行如下操作:

User user = session.get(User.class, 1);
System.out.println(user.getRoles().size());//打印1
session.delete(user);

我们会看到控制台记录hibernate使用了如下sql语句:

Hibernate: delete from t_user3_t_role3 where User_id=?
Hibernate: delete from t_role3 where id=?
Hibernate: delete from t_user3 where id=?
这里主要想说明的是,如果我们设置了级联删除,不仅中间表的关系维护记录会被清除,通过另一方的记录也会被删除。可在实际应用中,我们往往不希望存在这种级联删除,这好比我要除去一条没有意义的角色,不小心把关联的用户也删除了,这不是我们想要的结果,因此要控制好级联关系。

下面我们来看一个正确关联关系配置:

/********************用户方**************/
@ManyToMany
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)//使用hibernate注解级联保存和更新
@JoinTable(name = "t_user_role",
joinColumns = {@JoinColumn(name = "user_id")},//JoinColumns定义本方在中间表的主键映射
inverseJoinColumns = {@JoinColumn(name = "role_id")})//inverseJoinColumns定义另一在中间表的主键映射
private Set<Role> roles;
/********************角色方**************/
@ManyToMany
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)//使用hibernate注解级联保存和更新
@JoinTable(name = "t_user_role",
joinColumns = {@JoinColumn(name = "role_id")},//JoinColumns定义本方在中间表的主键映射
inverseJoinColumns = {@JoinColumn(name = "user_id")})//inverseJoinColumns定义另一在中间表的主键映射
private Set<User> users;

在这里,我们将两边都设为主控方,这样两边都可以对关联关系进行维护。假如我们只需要User方维护关联关系,可以将角色方改成如下配置:

@ManyToMany(mapperBy = "roles")
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)//使用hibernate注解级联保存和更新
private Set<User> users;

上面是第一种建立多对多关系,但这样配置的缺点是,我们无法知道两者关联关系的上下文属性,比如授权时间等,针对这一需求,我们可以单独建立一张中间表,然后让用户表和角色表分别和中间表建立一对多关联关系,这样,我们可以在中间表中添加一些附加信息如授权开始时间、授权结束时间等,这在实际开发中是很有意义的。
关于这种方法的实现这里不再举例。关于一对多关联关系的配置实现可参考我前面的文章。

目录
相关文章
|
29天前
|
Java 数据库连接
hibernate注解实体类(Dept.java)
hibernate注解实体类(Dept.java)
20 1
|
29天前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(Dept.java)
Hibernate中使用Criteria查询及注解——(Dept.java)
20 1
|
6天前
|
JSON Java 数据库连接
Hibernate中使用@Lob 注解保存String[] 问题
Hibernate中使用@Lob 注解保存String[] 问题
9 2
|
29天前
|
Java 数据库连接
hibernate注解实体类(Emp.java)
hibernate注解实体类(Emp.java)
21 1
|
29天前
|
Java 数据库连接
hibernate注解的测试
hibernate注解的测试
13 1
|
29天前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——( EmpCondition)
Hibernate中使用Criteria查询及注解——( EmpCondition)
18 1
|
29天前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(DeptTest.java)
Hibernate中使用Criteria查询及注解——(DeptTest.java)
10 1
|
29天前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(Emp.hbm.xml)
Hibernate中使用Criteria查询及注解——(Emp.hbm.xml)
15 2
|
29天前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——(Emp.java)
Hibernate中使用Criteria查询及注解——(Emp.java)
13 0
|
29天前
|
Java 数据库连接 数据库
Hibernate5中实体映射命名策略
Hibernate5中实体映射命名策略
60 0