开发者社区> jeanheo> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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;

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
hibernate5(12)注解映射[4]一对一外键关联
<div class="markdown_views"> <p>在实际博客网站中,文章内容的数据量非常多,它会影响我们检索文章其它数据的时间,如查询发布时间、标题、类别的等。这个时候,我们可以尝试将文章内容存在另一张表中,然后建立起文章——文章内容的一对一映射</p> <p>一对一关联有两种方式,一种是外键关联,另一种是复合主键关联。</p> <h1 id="外键关联"
1270 0
hibernate5(10)注解映射[2]一对多单向关联
<div class="markdown_views"> <p>在上一篇文章里,我们从端方向一端建立关联关系,完成了从文章到作者的关联关系建立,但在实际的博客网站中,用户肯定还需要获取自己所写的文章,这时可以建立用户(一)对文章(多)的单向关联映射。 <br> 先来看我们的一方配置实例</p> <pre class="prettyprint"><code class="lan
1701 0
hibernate注解影射表
@MappedSuperclass的用法 用在实体的继承过程中的父类上; 父类Cat 1 package com.xj.model; 2 3 import javax.
763 0
hibernate之映射关系一对多(自身关联)
自身关联注意的问题:    1、关联多的一方必须是只有一个父类节点,如果存在多个父类节点,那么就不能采用自身关联,(字段冗余数据太多)    2、自身关联可以节省表的设计、    3、一的一端为父节点,没有外键    4、多的一端为子节点,并且存在外键    5、关联一对多自身关联外键key、mary-to-one、指向同一个对象同时也是同一外键字段  代码配置:      
1174 0
+关注
80
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载