n-n(多对多)的关联关系必须通过连接表实现。下面以商品种类和商品之间的关系,即一个商品种类下面可以有多种商品,一种商品又可以属于多个商品种类,分别介绍单向的n-n关联关系和双向的n-n关联关系。

单向的n-n关联关系

  如果仅使用两张数据表,是不能实现n-n的关联关系的,如下图:

商品ITEM_AA属于商品种类CATEGORY_AA,但是如果商品ITEM_AA又同时属于商品种类CATEGORY_BB呢,两张数据表无法实现这种关系,所以我们需要使用到连接表,如下图所示:

我们添加一张连接表,其中的每一条记录表示某一个商品和某一个商品种类的对应关系。 
单向的n-n关联关系的域模型和关系数据模型如下图所示:

下面来实现这种关系,首先新建两个类:

 1 public class Category {
2
3 private Integer id;
4 private String name;
5
6 private Set<Item> items = new HashSet<>();
7
8 //getters and setters
9 }
10
11 public class Item {
12
13 private Integer id;
14 private String name;
15
16 //getters and setters
17 }

生成并编辑映射文件: 
  
Category.hbm.xml

 1 <?xml version="1.0"?>
2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4
5 <hibernate-mapping package="com.atguigu.hibernate.n2n">
6
7 <class name="Category" table="CATEGORIES">
8
9 <id name="id" type="java.lang.Integer">
10 <column name="ID" />
11 <generator class="native" />
12 </id>
13
14 <property name="name" type="java.lang.String">
15 <column name="NAME" />
16 </property>
17
18 <!-- table: 指定中间表 -->
19 <set name="items" table="CATEGORIES_ITEMS">
20 <key>
21 <column name="C_ID" />
22 </key>
23 <!-- 使用 many-to-many 指定多对多的关联关系. column 执行 Set 集合中的持久化类在中间表的外键列的名称 -->
24 <many-to-many class="Item" column="I_ID"></many-to-many>
25 </set>
26
27 </class>
28 </hibernate-mapping>

Item.hbm.xml

 1 <?xml version="1.0"?>
2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4
5 <hibernate-mapping>
6
7 <class name="com.atguigu.hibernate.n2n.Item" table="ITEMS">
8
9 <id name="id" type="java.lang.Integer">
10 <column name="ID" />
11 <generator class="native" />
12 </id>
13
14 <property name="name" type="java.lang.String">
15 <column name="NAME" />
16 </property>
17 </class>
18 </hibernate-mapping>

与1-n关联关系类似,在映射时,必须在category类的映射文件中设置set节点。但是不同的是,在set节点中指定的表为连接表CATEGORIES_ITEMS,而不再是所关联的类Item所对应的表,而且,在set节点中设置的是many-to-many节点,而不再是one-to-many。many-to-many节点的class属性指定了items集合中存放的对象类型是Item,colume属性指定了连接表CATEGORIES_ITEMS中参照ITEMS表的外键是ITEM_ID。
现在随便一个测试程序,发现除了生成了表CATEGORIES和表ITEMS,还生成了连接表CATEGORIES_ITEMS,表结构为:

下面测试save操作和get操作: 
   
单向n-n关联关系的save操作:

 1 @Test
2 public void testSave(){
3 Category category1 = new Category();
4 category1.setName("C-AA");
5
6 Category category2 = new Category();
7 category2.setName("C-BB");
8
9 Item item1 = new Item();
10 item1.setName("I-AA");
11
12 Item item2 = new Item();
13 item2.setName("I-BB");
14
15 //设定关联关系
16 category1.getItems().add(item1);
17 category1.getItems().add(item2);
18
19 category2.getItems().add(item1);
20 category2.getItems().add(item2);
21
22 //执行保存操作
23 session.save(category1);
24 session.save(category2);
25
26 session.save(item1);
27 session.save(item2);
28 }

运行程序,根据控制台打印的sql语句,发现会先往CATEGORIES表和ITEMS表分别插入两条记录,然后再往连接表CATEGORIES_ITEMS插入四条记录,这是符合我们的预期的:

单向n-n关联关系的get操作:

1 @Test
2 public void testGet(){
3 Category category = (Category) session.get(Category.class, 1);
4 System.out.println(category.getName());
5
6 //需要连接中间表
7 Set<Item> items = category.getItems();
8 System.out.println(items.size());
9 }

同1-n、1-1中类似,先查询category会使用懒加载机制,不会立即加载items,只有使用到items的时候才会加载。当加载items的时候,是通过内连接连接中间表CATEGORIES_ITEMS进行查询的:

双向的n-n关联关系

  在双向的n-n关联关系中,两端的类都需要使用集合属性,即Category中要包含一个Set<Item>,Item中也要包含一个Set<Category>,,且在数据库中同样需要使用连接表连接。其域模型和关系数据模型如下:

 1 public class Category {
2
3 private Integer id;
4 private String name;
5
6 private Set<Item> items = new HashSet<>();
7
8 //getters and setters
9 }
10
11 public class Item {
12
13 private Integer id;
14 private String name;
15
16 private Set<Category> categories = new HashSet<>();
17
18 //getters and setters
19 }

生成并编辑映射文件: 
  
Category.hbm.xml

 1 <?xml version="1.0"?>
2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4
5 <hibernate-mapping package="com.atguigu.hibernate.n2n">
6
7 <class name="Category" table="CATEGORIES">
8
9 <id name="id" type="java.lang.Integer">
10 <column name="ID" />
11 <generator class="native" />
12 </id>
13
14 <property name="name" type="java.lang.String">
15 <column name="NAME" />
16 </property>
17
18 <!-- table: 指定中间表 -->
19 <set name="items" table="CATEGORIES_ITEMS">
20 <key>
21 <column name="C_ID" />
22 </key>
23 <!-- 使用 many-to-many 指定多对多的关联关系. column 执行 Set 集合中的持久化类在中间表的外键列的名称 -->
24 <many-to-many class="Item" column="I_ID"></many-to-many>
25 </set>
26
27 </class>
28 </hibernate-mapping>

Item.hbm.xml

 1 <?xml version="1.0"?>
2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4
5 <hibernate-mapping>
6
7 <class name="com.atguigu.hibernate.n2n.Item" table="ITEMS">
8
9 <id name="id" type="java.lang.Integer">
10 <column name="ID" />
11 <generator class="native" />
12 </id>
13
14 <property name="name" type="java.lang.String">
15 <column name="NAME" />
16 </property>
17
18 <set name="categories" table="CATEGORIES_ITEMS" inverse="true">
19 <key column="I_ID"></key>
20 <many-to-many class="com.atguigu.hibernate.n2n.Category" column="C_ID"></many-to-many>
21 </set>
22
23 </class>
24 </hibernate-mapping>

大部门内容和单向n-n中类似,只是需要在Item类中添加字段

1  private Set<Category> categories = new HashSet<>();

并且需要在Item类的映射文件中像Category映射文件中那样配置set节点。此外,还有一个很重要的地方,就是需要在Category和Item的其中一端的映射文件中的set节点中设置inverse=”true”属性,以放弃维护关联关系,否则两端都会维护默认关系,这在某些情况下会导致错误,下面会演示。
  现在生成数据表,表结构和单向n-n中一样:
下面测试save操作和get操作: 
   
双向n-n关联关系的save操作:

 1 Category category1 = new Category();
2 category1.setName("C-AA");
3
4 Category category2 = new Category();
5 category2.setName("C-BB");
6
7 Item item1 = new Item();
8 item1.setName("I-AA");
9
10 Item item2 = new Item();
11 item2.setName("I-BB");
12
13 //设定关联关系
14 category1.getItems().add(item1);
15 category1.getItems().add(item2);
16
17 category2.getItems().add(item1);
18 category2.getItems().add(item2);
19
20 item1.getCategories().add(category1);
21 item1.getCategories().add(category2);
22
23 item2.getCategories().add(category1);
24 item2.getCategories().add(category2);
25
26 //执行保存操作
27 session.save(category1);
28 session.save(category2);
29
30 session.save(item1);
31 session.save(item2);

运行程序,和单向n-n中一样,一共打印8条insert语句,并成功插入记录。但是,现在我们移除Item.hbm.xml文件中的inverse=”true”属性,再运行程序,会抛出异常。这是因为在默认情况下n-n的两端都会维护关联关系,当执行上述四条save代码后,category要维护关联关系,往连接表中添加4条记录,然后item也要维护关联关系,往连接表中添加相同的4条记录,会导致连接表中主键重复,解决方法就是在Item.hbm.xml文件中设置inverse=”true”属性。
双向n-n关联关系的get操作:

@Test
public void testGet(){
Category category = (Category) session.get(Category.class, 1);
System.out.println(category.getName()); //需要连接中间表
Set<Item> items = category.getItems();
System.out.println(items.size());
}

双向n-n关联关系的get操作和单向n-n中一样,包含懒加载机制和内连接。 
  补充一下,双向n-n中两端的映射文件的字段对应如下图所示:

完事!

hibernate多对多单向(双向)关系映射的更多相关文章

  1. hibernate多对一单向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  2. Java进阶知识11 Hibernate多对多单向关联(Annotation+XML实现)

    1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,但是老师不知道自己教哪些学生时,可用单向关联 1.2.创建Teacher类和Student ...

  3. Java进阶知识08 Hibernate多对一单向关联(Annotation+XML实现)

    1.Annotation 注解版 1.1.在多的一方加外键 1.2.创建Customer类和Order类 package com.shore.model; import javax.persisten ...

  4. hibernate多对一单向外键

    hibernate多对一单向外键: 描述:

  5. Hibernate多对多单向关联和双向关联 --Hibernate框架

    Hibernate关联关系中相对比较特殊的就是多对多关联,多对多关联与一对一关联和一对多关联不同,多对多关联需要另外一张映射表用于保存多对多映射信息.本例介绍多对多单向关联和双向关联.单向关联 :指具 ...

  6. Hibernate基于注解的双向one-to-many映射关系的实现

    在项目中用到了一对多的实体类关系映射,之前接触的都是基于配置文件的映射实现.可是公司的大部分都是基于注解的.因此自己參考之前的代码捣鼓了基于注解的一对多的映射关系实现. 背景: 一的一端:QingAo ...

  7. Hibernate 再接触 多对多单向双向关联

    情景:一个老师可能有多个学生,一个学生也可能有多个老师 多对一单向: 例如老师知道自己教哪些学生,学生却不知道自己被哪些老师教 方法:使用第三张表 分别存两张表的id annotation Stude ...

  8. Hibernate -- 注解(Annotation)关系映射

    转自:http://www.cnblogs.com/tyler2000/archive/2011/01/20/1940354.html 1. Hibernate Annotation关系映射有下面几种 ...

  9. Hibernate基础学习(五)—对象-关系映射(下)

    一.单向n-1 单向n-1关联只需从n的一端可以访问1的一端. 域模型: 从Order到Customer的多对一单向关联.Order类中定义一个Customer属性,而在Customer类不用存放Or ...

  10. Java之旅hibernate(8)——基本关系映射

    何为关系,何为映射,关系这个词想必大家都不陌生.比方你和老师之间是师生关系,你和父母之间是父子或者父女(母子或者母女关系). 关系是存在某种联系物体之间产生的.什么都是可能的.比方你和工具,你仅仅能使 ...

随机推荐

  1. Python 常见运算符表达式

    常见运算符表达式    1.算数运算符    2.逻辑运算符    3.比较运算符    4.成员运算符    5.位运算符    6.身份运算符a.赋值运算符 =    格式:变量= 表达式     ...

  2. Centos8 Docker部署 .Net6 项目

    .Net6项目发布 1.在VS中发布项目,并编写好Dockerfile文件 Dockerfile文件内容如下: FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS ...

  3. littlevgl架构浅析

    一.   littlevgl有几个线程,作用是什么? 三个,主线程一个,和在主线程的hal_init函数中创建的另两个sdl线程. 主线程完成一系列初始化工作后,循环每10ms调用在lv_init函数 ...

  4. 【JavaSE】Java基础·疑难点汇集

    Java基础·疑难点 2019-08-03  19:51:39  by冲冲 1. 部分Java关键字 instanceof:用来测试一个对象是否是指定类型的实例. native:用来声明一个方法是由与 ...

  5. 撸了一个可调试 gRPC 的 GUI 客户端

    前言 平时大家写完 gRPC 接口后是如何测试的?往往有以下几个方法: 写单测代码,自己模拟客户端测试. 可以搭一个 gRPC-Gateway 服务,这样就可以在 postman 中进行模拟. 但这两 ...

  6. 理解ASP.NET Core - 过滤器(Filters)

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 Filter概览 如果你是从ASP.NET一路走过来的,那么你一定对过滤器(Filter)不陌 ...

  7. 为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

    大家好,我是小林. Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. ...

  8. 【2020五校联考NOIP #6】最佳观影

    题意: 给出一个 \(k \times k\) 的网格和 \(n\) 次操作.其中 \(k\) 为奇数. 每次操作给出一个数 \(m\).每次你要找出一个三元组 \((x,l,r)\) 使得: \(r ...

  9. DP 优化方法大杂烩 & 做题记录 I.

    标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...

  10. CMSIS-RTOS 信号量Semaphores

    信号量Semaphores 和信号类似,信号量也是一种同步多个线程的方式,简单来讲,信号量就是装有一些令牌的容器.当一个线程在执行过程中,就可能遇到一个系统调用来获取信号量令牌,如果这个信号量包含多个 ...