Hibernate一对多的关联映射,详解(代码+图解)与应用 举个栗子,搞的清楚
发布日期:2021-06-29 15:01:39 浏览次数:2 分类:技术文章

本文共 13439 字,大约阅读时间需要 44 分钟。

Hibernate一对多的关联映射

一、数据库表与表之间的关系

1、一对多关系

(1)什么样的关系是属于一对多

一个部门可以对应多个员工,一个员工只能属于某一个部门
一个客户对应多个联系人,一个联系人只能属于某一个客户
游戏当中我们组队的时候,在组队的那段时间当中,一个队员只能属于一个队伍,但是一个队伍可以拥有多个队员
在这里插入图片描述
(2)一对多建表原则:
在这里插入图片描述

2、多对多关系

(1)什么样的关系是属于多对多

一个学生可以选择多门课程,一门课程也可以被多个学生所选择。
一个用户可以选择多个角色,一个角色也可以被多个用户所选择。
(2)多对多键表原则
在这里插入图片描述

3、一对一关系

(1)什么样的关系是属于一对一

一个公司只能有一个注册地址,一个注册地址只能被一个公司注册。
(2)一对一键表原则
在这里插入图片描述
在这里插入图片描述

二、实际应用(创建表与类的关系)

1、创建项目引入架包

在这里插入图片描述

2、创建数据库和表

创建一对多数据库关系的表

(1)创建客户表

CREATE TABLE `cst_customer` (  `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',  `cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',  `cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',  `cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',  `cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',  `cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',  `cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',  PRIMARY KEY (`cust_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

(2)创建联系人的表

CREATE TABLE `cst_linkman` (  `lkm_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',  `lkm_name` VARCHAR(16) DEFAULT NULL COMMENT '联系人姓名',  `lkm_cust_id` BIGINT(32) DEFAULT NULL COMMENT '客户id',  `lkm_gender` CHAR(1) DEFAULT NULL COMMENT '联系人性别',  `lkm_phone` VARCHAR(16) DEFAULT NULL COMMENT '联系人办公电话',  `lkm_mobile` VARCHAR(16) DEFAULT NULL COMMENT '联系人手机',  `lkm_email` VARCHAR(64) DEFAULT NULL COMMENT '联系人邮箱',  `lkm_qq` VARCHAR(16) DEFAULT NULL COMMENT '联系人qq',  `lkm_position` VARCHAR(16) DEFAULT NULL COMMENT '联系人职位',  `lkm_memo` VARCHAR(512) DEFAULT NULL COMMENT '联系人备注',  PRIMARY KEY (`lkm_id`),  KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),  CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

在这里插入图片描述

(3)创建实体
一的一方的实体,当中放置多的一方的集合
在这里插入图片描述
代码如下:
在这里插入图片描述
多的一方的实体,放置一的一方的对象

在这里插入图片描述

代码如下:
在这里插入图片描述
同时分别生成set和get方法

(4)创建映射文件

在项目文件夹下创建映射文件
在这里插入图片描述
多的一方的映射文件
LinkMan.hbm.xml

一的一方的映射文件

Customer.hbm.xml

4、配置核心配置文件Customer.hbm.xml

com.mysql.jdbc.Driver
jdbc:mysql:///hibernate_day03
root
root
org.hibernate.dialect.MySQLDialect
true
true
update
org.hibernate.connection.C3P0ConnectionProvider
5
20
120
3000
4
thread

5、log4j.properties

### direct log messages to stdout ###log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.errlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d{
ABSOLUTE} %5p %c{
1}:%L - %m%n### direct messages to file mylog.log ###log4j.appender.file=org.apache.log4j.FileAppenderlog4j.appender.file.File=c\:mylog.loglog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=%d{
ABSOLUTE} %5p %c{
1}:%L - %m%n### set log levels - for more verbose logging change 'info' to 'debug' #### error warn info debug tracelog4j.rootLogger= info, stdout

6、配置工具类

package com.itzheng.hibernate.utils;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;/*Hibernate的工具类做hibernate的小练习,对configuration的configure()方法很好奇,为啥创建的对象还要执行这个方法呢。Configuration cfg = new Configuration().configure();原来configure()方法默认会在classpath下面寻找hibernate.cfg.xml文件,如果没有找到该文件,系统会打印如下信息并抛出HibernateException异常。其实不使用configure()方法也可以Configuration cfg = new Configuration();这时hibernate会在classpath下面寻找hibernate.properties文件,如果没有找到该文件,系统会打印如下信息并抛出HibernateException异常。 */public class HibernateUtils {
public static final Configuration cfg; public static final SessionFactory sf; static {
cfg = new Configuration().configure();// 获取与数据库的链接的配置文件 sf = cfg.buildSessionFactory();//开启事务建立与数据库之间的链接 } public static Session openSession() {
return sf.openSession(); } public static Session getCurrentSession() {
return sf.getCurrentSession(); }}

三、实际应用

1、编写测试类(一对多的基本配置和操作)

package com.itzheng.hibernate.demo1;import org.hibernate.Session;import org.hibernate.Transaction;import org.junit.Test;import com.itzheng.hibernate.domain.Customer;import com.itzheng.hibernate.domain.LinkMan;import com.itzheng.hibernate.utils.HibernateUtils;/* * 一对多的测试类 */public class HibernateDemo1 {
@Test // 保存两个客户和三个联系人 并且建立好关系 public void demo01() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); // 创建两个客户 Customer customer1 = new Customer(); customer1.setCust_name("王东"); Customer customer2 = new Customer(); customer2.setCust_name("赵洪"); // 创建3个联系人 LinkMan linkMan1 = new LinkMan(); linkMan1.setLkm_name("分解"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("如花"); LinkMan linkMan3 = new LinkMan(); linkMan3.setLkm_name("旺财"); // 设置关系 linkMan1.setCustomer(customer1);// 将客户放入到联系人当中 linkMan2.setCustomer(customer1); linkMan3.setCustomer(customer2); customer1.getLinkMans().add(linkMan1);// 将联系人放入到客户的集合当中 customer1.getLinkMans().add(linkMan2); customer2.getLinkMans().add(linkMan3); // 保存数据 session.save(linkMan1); session.save(linkMan2); session.save(linkMan3); session.save(customer1); session.save(customer2); transaction.commit(); }}

2、Hibernate的一对多相关的操作

(1)一对多的关系只保存一遍是否可以:

更改核心配置文件
在这里插入图片描述
测试代码

@Test	// 一对多关系只保存一边 是否可以	public void demo2() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Customer customer = new Customer(); customer.setCust_name("赵洪"); LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("如花"); customer.getLinkMans().add(linkMan); linkMan.setCustomer(customer); //只保存一边是否可以,不可以。 session.save(customer);//只保存客户 transaction.commit(); }

当只保存客户时:报错:瞬时对象异常:持久态对象关联了瞬时太的对象

customer —>save之后变成持久的了 但是LinkMan 还是瞬时的
在这里插入图片描述
当只保存联系人的时候:报错
在这里插入图片描述
解决办法通过级联的方式,保证只操控一个对象的时候就可以,同时操作其关联的对象
(2)一对多的级联操作:

什么吗叫做级联

级联指的是,我们在操作一个对象的时候,会同时操作其关联的对象。
级联是有方向性的
a、操作一的一方的时候,会操作到多的一方。
b、操作多的一方的时候,会操作到一的一方。

3、级联保存或者更新

(1)保存客户级联联系人(一的一方配置级联)

级联保存或更新的操作
保存客户级联联系人
操作的主体对象是客户对象,需要在客户的映射文件Customer.hbm.xml中 进行配置
cascade配置级联
在这里插入图片描述

@Test	// 级联保存或更新的操作	//保存客户级联联系人	//操作的主体对象是客户对象,需要在客户的映射文件Customer.hbm.xml中 进行配置 	public void demo3() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Customer customer = new Customer(); customer.setCust_name("赵洪"); LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("如花"); customer.getLinkMans().add(linkMan); linkMan.setCustomer(customer); //只保存一边是否可以,需要级联联系人,保证操作客户就可以同时操作联系人 session.save(customer);//只保存客户 transaction.commit(); }

(2)保存联系人级联客户(多的一方配置级联)

级联保存或更新的操作
保存联系人级联客户
操作的主体对象是客户对象,需要在客户的映射文件LinkMan.hbm.xml中 进行配置
在这里插入图片描述

@Test	// 级联保存或更新的操作	// 保存联系人级联客户	// 操作的主体对象是客户对象,需要在客户的映射文件LinkMan.hbm.xml中 进行配置	public void demo4() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Customer customer = new Customer(); customer.setCust_name("铁蛋"); LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("小花"); customer.getLinkMans().add(linkMan); linkMan.setCustomer(customer); // 只保存一边是否可以,需要级联联系人,保证操作客户就可以同时操作联系人 session.save(linkMan);// 只保联系人 transaction.commit(); }

(3)测试对象的导航

测试对象的导航
前提:一对多的双方都设置了cascade=“save-update”

@Test// 测试对象的导航// 前提:一对多的双方都设置了cascade="save-update"public void demo5() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Customer customer = new Customer(); customer.setCust_name("铁蛋"); LinkMan linkMan1 = new LinkMan(); linkMan1.setLkm_name("小花"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("大花"); LinkMan linkMan3 = new LinkMan(); linkMan3.setLkm_name("二花"); linkMan1.setCustomer(customer);// 1号连续人关联客户 customer.getLinkMans().add(linkMan2);// 客户关联二号联系人 customer.getLinkMans().add(linkMan3);// 客户关联三号联系人 // 双方都设置了cascade session.save(linkMan1);// 发送4条insert语句,当保存一号联系人的时候,同时会将客户,二号联系人和三号联系都保存进去 transaction.commit();}

发送4条insert语句,当保存一号联系人的时候,同时会将客户,二号联系人和三号联系都保存进去

在这里插入图片描述
如果存储的是customer对象
在这里插入图片描述
发送3条insert语句 存储 客户 联系人2 联系人3
在这里插入图片描述
如果存储的是linkMan2对象
在这里插入图片描述
发送一条insert语句
在这里插入图片描述

4、级联删除

1、级联删除:

删除一边的时候,同时会删除另一方的数据也一并删除。
2、删除客户级联删除联系人
(1)先往数据库当中保存数据

@Test	// 保存两个客户和三个联系人 并且建立好关系	public void demo01() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); // 创建两个客户 Customer customer1 = new Customer(); customer1.setCust_name("王东"); Customer customer2 = new Customer(); customer2.setCust_name("赵洪"); // 创建3个联系人 LinkMan linkMan1 = new LinkMan(); linkMan1.setLkm_name("分解"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("如花"); LinkMan linkMan3 = new LinkMan(); linkMan3.setLkm_name("旺财"); // 设置关系 linkMan1.setCustomer(customer1);// 将客户放入到联系人当中 linkMan2.setCustomer(customer1); linkMan3.setCustomer(customer2); customer1.getLinkMans().add(linkMan1);// 将联系人放入到客户的集合当中 customer1.getLinkMans().add(linkMan2); customer2.getLinkMans().add(linkMan3); // 保存数据 session.save(linkMan1); session.save(linkMan2); session.save(linkMan3); session.save(customer1); session.save(customer2); transaction.commit(); }

在数据库当中直接删除客户是无法删除的,因为如果要删除客户首先要删除对应的联系人,之后才能删除客户(因为在创建联系人的时候设置了外键)

3、默认情况下删除
在这里插入图片描述

@Test	//级联删除:	//删除客户级联删除联系人	public void demo6() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //没有设置级联删除:默认情况 Customer customer = session.get(Customer.class,1l);//要想删除线查询对应的内容 session.delete(customer); transaction.commit(); }

默认情况下删除客户会先将联系人表当中的外键置空,然后删除客户对应表当中的数据

在这里插入图片描述
4、自定义情况下级联删除删除客户同时删除联系人
级联删除:
删除客户级联删除联系人,删除的主体是客户,需要在客户的映射文件当中Customer.hbm.xml配置
update保存或者更新
delete级联删除
在这里插入图片描述

@Test	// 级联删除:	// 删除客户级联删除联系人,删除的主体是客户,需要在客户的映射文件当中Customer.hbm.xml配置	//	public void demo6() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); // 没有设置级联删除:默认情况 // Customer customer = session.get(Customer.class,1l);//要想删除线查询对应的内容 // session.delete(customer); Customer customer = session.get(Customer.class, 1l);// 要想删除线查询对应的内容 session.delete(customer); transaction.commit(); }

在这里插入图片描述

5、删除联系人的时候同时级联删除客户(基本不用)
级联删除:
删除联系人级联删除客户,删除的主体是联系人,需要在联系人的映射文件当中LinkMan.hbm.xml配置
在这里插入图片描述

@Test	// 级联删除:	// 删除联系人级联删除客户,删除的主体是联系人,需要在联系人的映射文件当中LinkMan.hbm.xml配置	public void demo7() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //删除联系人的同时删除客户 LinkMan linkMan = session.get(LinkMan.class, 3l);//查询对应id的联系人对象 session.delete(linkMan); transaction.commit(); }

在这里插入图片描述

5、在一对多当中设置了双向的关联,产生一些多余的SQL语句

(1)举个栗子

在这里插入图片描述
运行demo1,创建两个客户

@Test	// 保存两个客户和三个联系人 并且建立好关系	public void demo01() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); // 创建两个客户 Customer customer1 = new Customer(); customer1.setCust_name("王东"); Customer customer2 = new Customer(); customer2.setCust_name("赵洪"); // 创建3个联系人 LinkMan linkMan1 = new LinkMan(); linkMan1.setLkm_name("分解"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkm_name("如花"); LinkMan linkMan3 = new LinkMan(); linkMan3.setLkm_name("旺财"); // 设置关系 linkMan1.setCustomer(customer1);// 将客户放入到联系人当中 linkMan2.setCustomer(customer1); linkMan3.setCustomer(customer2); customer1.getLinkMans().add(linkMan1);// 将联系人放入到客户的集合当中 customer1.getLinkMans().add(linkMan2); customer2.getLinkMans().add(linkMan3); // 保存数据 session.save(linkMan1); session.save(linkMan2); session.save(linkMan3); session.save(customer1); session.save(customer2); transaction.commit(); }

在这里插入图片描述

public void demo8() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); // 查询二号联系人 LinkMan linkMan = session.get(LinkMan.class, 2l); // 查询2号客户 Customer customer = session.get(Customer.class, 2l); // 双向的关联 // 将查询到的客户设置到2号联系人当中 linkMan.setCustomer(customer);// 直接将对象放入到linkMan当中 // 将新设置的2号联系人放入到2号客户当中 customer.getLinkMans().add(linkMan);// 获取到linkMan集合将对象添加进去 transaction.commit(); }

重复修改lkm_cust_id

在这里插入图片描述
更改Linkman和Customer都活修改外键
在这里插入图片描述
产生多余的SQL语句
(2)解决产生多余SQL语句
a、单向维护:
b、使其一方放弃外键维护权
使一的一方放弃外键维护权,多的一方维护和一的关系
在Customer.hbm.xml上
设置inverse=“true” 默认情况是false,设置为true为放弃外键使用权
在这里插入图片描述
再次执行上述demo8,只发送一次update语句,修改一次外键,而且是多的一方在变换时修改的外键
在这里插入图片描述
c、在一对多关联查询修改的时候。

6、区分cascade和inverse

核心配置文件hibernate.cfg.xml

在这里插入图片描述
LinkMan.hbm.xml当中
在这里插入图片描述
Customer.hbm.xml
在这里插入图片描述
测试类

@Test	// 区分cascade和inverse的区别	public void demo9() {
Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); Customer customer = new Customer(); customer.setCust_name("李冰"); LinkMan linkMan = new LinkMan(); linkMan.setLkm_name("分解"); customer.getLinkMans().add(linkMan);//获取到list集合将对象放入到集合当中 //条件是在Customer.hbm.xml的set当中配置了cascade="save-update" inverse="true" session.save(customer);//客户会插入到数据库,联系人也会插入到数据库,但是外键为null,因为cascade没有控制外键的权利 transaction.commit(); }

客户会插入到数据库,联系人也会插入到数据库,但是外键为null,因为cascade没有控制外键的权利

在这里插入图片描述

转载地址:https://code100.blog.csdn.net/article/details/105799609 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:一张图快速理解入门递归函数(Java版)
下一篇:10分钟解决Hibernate的事务管理,Hibernate当中要设置事务的隔离级别

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月27日 11时48分41秒