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

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

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

关于这种方法的实现这里不再举例。关于一对多关联关系的配置实现可参考我前面的文章。

时间: 2024-11-21 05:27:49

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

hibernate5(13)注解映射[5]一对一共享主键关联

一对一共享主键 下面我们直接通过实例来讲解共享主键配置: 主键主控方:Article package com.zeng2.model; @Table(name = "t_article2") @Entity public class Article { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private String title; @OneToOne(cascade = Casc

hibernate5(15)注解映射[7]集合关联

集合注解映射 集合关系映射可以看成是一对多关系映射的一种简化,在一个电商系统里,出售的产品可能会有多张展示图片,如果我们使用一对多来建立关联映射时,需要创建一个实体类Images,里面可能有属性:图片在服务器的访问路径url和图片所属产品productId.但如果我们使用集合关系映射,则无需新建一个实体类,只需在Product中定义一个集合成员属性即可. Set集合 在产品中,我们的图片路径一般是不会相同的,我们可以使用Set集合来建立映射 我们下面来看这一需求的配置示例: @Entity @T

Android ORM对象关系映射之GreenDAO建立多表关联

https://blog.csdn.net/u010687392/article/details/48496299 利用GreenDAO可以非常方便的建立多张表之间的关联 一对一关联 通常我们在操作数据库的时候,我们往往不是单独的对一张表进行操作,而是对这张表的操作会联动的影响另外一张表或者多张表,比如:现在有两张表,一张是用户User表(有name.age.sex三个字段),一张是头像Picture表(有pictureId.pictureName.width.height四个字段).假如用户表

hibernate5(10)注解映射[2]一对多单向关联

在上一篇文章里.我们从端方向一端建立关联关系,完毕了从文章到作者的关联关系建立.但在实际的博客站点中,用户肯定还须要获取自己所写的文章,这时能够建立用户(一)对文章(多)的单向关联映射. 先来看我们的一方配置实例 package com.zeng.model; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.F

hibernate5(9)注解映射[1]一对多单向关联

一对多单向关联映射 在实际的博客网站中,用户肯定需要获取自己所写的文章,这时可以建立用户(一)对文章(多)的单向关联映射. 先来看我们的一方配置实例 package com.zeng.model; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persisten

hibernate5(12)注解映射[4]一对一外键关联

在实际博客站点中,文章内容的数据量非常多,它会影响我们检索文章其他数据的时间,如查询公布时间.标题.类别的等. 这个时候,我们能够尝试将文章内容存在还有一张表中,然后建立起文章--文章内容的一对一映射 一对一关联有两种方式,一种是外键关联.还有一种是复合主键关联. 外键关联 以下我们先看一个一对一单向关联的实例 /*************关联关系维护方************/ @Table(name = "t_article") @Entity public class Artic

10、JPA_映射双向多对多的关联关系

双向多对多的关联关系 双向多对多的关联关系(抽象成A-B)具体体现:A中有B的集合的引用,同时B中也有对A的集合的引用.A.B两个实体对应的数据表靠一张中间表来建立连接关系. 同时我们还知道,双向多对多的关联关系可以拆分成三张表,两个双向多对一关联关系.拆分以后还是有一张中间表,其好处就是可以在中间表中添加某些属性用作其它.这个后面会讲解.而单纯的双向多对多关联关系的中间表有两个外键列,无法增加其它属性. 本节只讲单纯的双向多对多关联关系.从例子讲解配置方法和原理: 有“商品Item”和“类别C

6、JPA_映射单向多对一的关联关系(n的一方有1的引用,1的一方没有n的集合属性)

单向多对一的关联关系 具体体现:n的一方有1的引用,1的一方没有n的集合属性 举个例子:订单Order和顾客Customer是一个单向多对一的关联关系.Order是n的一方,有对Customer的引用:而Customer作为1的一方却没有Order的集合属性. 主要是对n的一方使用@ManyToOne和@JoinColumn注解.而1的一方不需要做任何修改.具体的映射方法: 1.采用@ManyToOne注解映射多对一的关联关系,默认情况下采用“左外连接”的方式进行加载.可以通过配置@ManyTo

hibernate关系映射(多对多)

多对多关系(学生Student,课程Course) 学生类的定义以及hbm文件的配置如下 1 public class Student { 2 private int id; 3 private String name; 4 private Set<Course> courses = new HashSet<Course>(); 5 } 1 <?xml version="1.0" encoding="UTF-8"?> 2 <