hibernate多对多单向(双向)关系映射
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多对多单向(双向)关系映射的更多相关文章
- hibernate多对一单向关联
关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...
- Java进阶知识11 Hibernate多对多单向关联(Annotation+XML实现)
1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,但是老师不知道自己教哪些学生时,可用单向关联 1.2.创建Teacher类和Student ...
- Java进阶知识08 Hibernate多对一单向关联(Annotation+XML实现)
1.Annotation 注解版 1.1.在多的一方加外键 1.2.创建Customer类和Order类 package com.shore.model; import javax.persisten ...
- hibernate多对一单向外键
hibernate多对一单向外键: 描述:
- Hibernate多对多单向关联和双向关联 --Hibernate框架
Hibernate关联关系中相对比较特殊的就是多对多关联,多对多关联与一对一关联和一对多关联不同,多对多关联需要另外一张映射表用于保存多对多映射信息.本例介绍多对多单向关联和双向关联.单向关联 :指具 ...
- Hibernate基于注解的双向one-to-many映射关系的实现
在项目中用到了一对多的实体类关系映射,之前接触的都是基于配置文件的映射实现.可是公司的大部分都是基于注解的.因此自己參考之前的代码捣鼓了基于注解的一对多的映射关系实现. 背景: 一的一端:QingAo ...
- Hibernate 再接触 多对多单向双向关联
情景:一个老师可能有多个学生,一个学生也可能有多个老师 多对一单向: 例如老师知道自己教哪些学生,学生却不知道自己被哪些老师教 方法:使用第三张表 分别存两张表的id annotation Stude ...
- Hibernate -- 注解(Annotation)关系映射
转自:http://www.cnblogs.com/tyler2000/archive/2011/01/20/1940354.html 1. Hibernate Annotation关系映射有下面几种 ...
- Hibernate基础学习(五)—对象-关系映射(下)
一.单向n-1 单向n-1关联只需从n的一端可以访问1的一端. 域模型: 从Order到Customer的多对一单向关联.Order类中定义一个Customer属性,而在Customer类不用存放Or ...
- Java之旅hibernate(8)——基本关系映射
何为关系,何为映射,关系这个词想必大家都不陌生.比方你和老师之间是师生关系,你和父母之间是父子或者父女(母子或者母女关系). 关系是存在某种联系物体之间产生的.什么都是可能的.比方你和工具,你仅仅能使 ...
随机推荐
- dotNET5的MVC页面传值方式总结
本文大致讲解mvc前后端的传值方式,包括control向view.view向control.以及action向action. 一.经典回顾 二.Controller向View传值 1. ViewBag ...
- 说透 Docker:虚拟化
本章内容将讲解 Docker 虚拟化.虚拟化本质.namespace.cgroups. Docker 虚拟化 关于Docker 本小节将介绍 Docker 虚拟化的一些特点. Docker 是一个开放 ...
- 通过python来获取网页状态
#!/usr/bin/python import sys,httplibfrom optparse import OptionParserusageString = "Usage: %pro ...
- Docker部署 Mysql .Net6等容器
Centos8安装Docker 1.更新一下yum [root@VM-24-9-centos ~]# yum -y update 2.安装containerd.io # centos8默认使用podm ...
- [luogu6185]序列
对于2操作,如果把这些操作看成边,那么对于某一个连通块内的若干个点,满足权值可以任意分配(证明:归纳,若n个点可以,那么先将新增的点调整好,再对原来n个点重新分配即可),因此可以将原图缩点,并将连通块 ...
- [bzoj4651]网格
考虑将最上中最左的跳蚤孤立,容易发现他的上面和左面没有跳蚤,因此只需要将他的右边和下边替换掉就可以了答案为-1有两种情况:1.c>=n*m-1;2.c=n*m-2且这两只跳蚤相邻对于其他情况,将 ...
- [loj4]Quine
很有趣的一道题目,如何让一个程序输出自身如果用字符串s表示程序,那么意味着可以通过s来输出sprintf是一个可以利用的函数,相当于要求printf(s,s)输出的就是s那么只需要在s中加入%c和%d ...
- 青龙+Nvjdc短信登陆对接Xdd-plus推送+Ninja CK登陆教程(11.23更新)
一.准备工作 1.shh工具(powshell.gitbash等等) 2.购买一台云服务器(阿里云.腾讯云都可以) 3.安装宝塔面板 宝塔Linux面板安装教程 - 2021年8月18日更新 - 7. ...
- appScan安全软件的使用
1.点击文件,新建 2.常规扫描 3.点击下一步 4.输入需要扫描的网站 5.下一步,如果有账号 密码可以使用记录. 6.选择缺省值,下一步 7.启动全面扫描.
- idea中解决整合SSM加载不到dataSource;
idea在搭建maven的ssm项目中注入dataSource报错解决方案: 在整合ssm时候,发现 dataSource加载不到,并报错:解决办法为:file–>project structu ...