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;

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

目录
相关文章
|
5月前
|
Java 数据库连接
hibernate注解实体类(Dept.java)
hibernate注解实体类(Dept.java)
|
1月前
|
Java 数据库连接 API
解锁高效开发秘籍:深入探究 Hibernate 如何优雅处理一对多与多对多关系,让数据映射再无烦恼!
【9月更文挑战第3天】Hibernate 是 Java 领域中最流行的 ORM 框架之一,广泛用于处理实体对象与数据库表之间的映射。尤其在处理复杂关系如一对多和多对多时,Hibernate 提供了丰富的 API 和配置选项。本文通过具体代码示例,展示如何使用 `@OneToMany`、`@JoinColumn`、`@ManyToMany` 和 `@JoinTable` 等注解优雅地实现这些关系,帮助开发者保持代码简洁的同时确保数据一致性。
29 4
|
2月前
|
XML JSON Java
使用IDEA+Maven搭建整合一个Struts2+Spring4+Hibernate4项目,混合使用传统Xml与@注解,返回JSP视图或JSON数据,快来给你的SSH老项目翻新一下吧
本文介绍了如何使用IntelliJ IDEA和Maven搭建一个整合了Struts2、Spring4、Hibernate4的J2EE项目,并配置了项目目录结构、web.xml、welcome.jsp以及多个JSP页面,用于刷新和学习传统的SSH框架。
36 0
使用IDEA+Maven搭建整合一个Struts2+Spring4+Hibernate4项目,混合使用传统Xml与@注解,返回JSP视图或JSON数据,快来给你的SSH老项目翻新一下吧
|
2月前
|
Java 数据库连接 数据库
AI 时代风起云涌,Hibernate 实体映射引领数据库高效之路,最佳实践与陷阱全解析!
【8月更文挑战第31天】Hibernate 是一款强大的 Java 持久化框架,可将 Java 对象映射到关系数据库表中。本文通过代码示例详细介绍了 Hibernate 实体映射的最佳实践,包括合理使用关联映射(如 `@OneToMany` 和 `@ManyToOne`)以及正确处理继承关系(如单表继承)。此外,还探讨了常见陷阱,例如循环依赖可能导致的无限递归问题,并提供了使用 `@JsonIgnore` 等注解来避免此类问题的方法。通过遵循这些最佳实践,可以显著提升开发效率和数据库操作性能。
68 0
|
2月前
|
数据库 开发者 Java
Hibernate映射注解的魔力:实体类配置的革命,让你的代码量瞬间蒸发!
【8月更文挑战第31天】Hibernate 是一款出色的对象关系映射框架,简化了 Java 应用与数据库的交互。其映射注解让实体类配置变得直观简洁。本文深入剖析核心概念与使用技巧,通过示例展示如何简化配置。
24 0
|
4月前
|
JSON Java 数据库连接
Hibernate中使用@Lob 注解保存String[] 问题
Hibernate中使用@Lob 注解保存String[] 问题
28 2
|
5月前
|
Java 数据库连接
hibernate注解实体类(Emp.java)
hibernate注解实体类(Emp.java)
|
5月前
|
Java 数据库连接
hibernate注解的测试
hibernate注解的测试
|
5月前
|
Java 数据库连接
Hibernate中使用Criteria查询及注解——( EmpCondition)
Hibernate中使用Criteria查询及注解——( EmpCondition)
|
5月前
|
SQL 缓存 Java
框架分析(9)-Hibernate
框架分析(9)-Hibernate