五、hibernate表与表之间的关系(一对多关系)
2024-09-07 05:38:05
数据库表与表之间的关系
一对多:一个学校可以有多个学生,一个学生只能有一个学校
多对多:一个学生可以有多个老师,一个老师可以教多个学生
一对一:一个人只能有一个身份证号,一个身份证号只能找到一个人
一对多关系
创建学生和学校表
create table school(
sch_id int PRIMARY KEY auto_increment ,
sch_name VARCHAR(30),
sch_address VARCHAR(200)
); create table student(
sid int PRIMARY KEY auto_increment,
s_sch int ,
sname VARCHAR(30),
FOREIGN KEY(s_sch) REFERENCES school(sch_id)
);
根据表创建实体类和映射文件
一的一方
School.java
package com.qf.entity; import java.util.HashSet;
import java.util.Set; public class School {
private long sch_id;
private String sch_name;
private String sch_address;
//一个学校有多个学生,应该是一对多关系中多的一方的集合,hibernate默认使用set集合
private Set<Student> stus = new HashSet<Student>(); //get、set方法
public long getSch_id() {
return sch_id;
}
public void setSch_id(long sch_id) {
this.sch_id = sch_id;
}
public String getSch_name() {
return sch_name;
}
public void setSch_name(String sch_name) {
this.sch_name = sch_name;
}
public String getSch_address() {
return sch_address;
}
public void setSch_address(String sch_address) {
this.sch_address = sch_address;
}
public Set<Student> getStus() {
return stus;
}
public void setStus(Set<Student> stus) {
this.stus = stus;
} @Override
public String toString() {
return "School [sch_id=" + sch_id + ", sch_name=" + sch_name + ", sch_address=" + sch_address + ", stus=" + stus
+ "]";
}
public School() {
super();
} }
School.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 配置表与实体的映射关系 -->
<class name="com.qf.entity.School" table="school">
<id name="sch_id" column="sch_id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="sch_name" column="sch_name"/>
<property name="sch_address" column="sch_address"/> <!-- name:多的一方的集合属性的名称 -->
<set name="stus" >
<!-- column:多的一方表的外键名称-->
<key column="s_sch"></key>
<!-- class:多的一方的类的全路径 -->
<one-to-many class="com.qf.entity.Student"/>
</set>
</class>
</hibernate-mapping>
多的一方
Student.java
package com.qf.entity; public class Student { private Long sid;
private String sname;
//一个学生只能有一个学校
private School sch; public Student() {
super();
} public Long getSid() {
return sid;
}
public void setSid(Long sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public School getSch() {
return sch;
}
public void setSch(School sch) {
this.sch = sch;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", sch=" + sch + "]";
} }
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 配置表与实体的映射关系 -->
<class name="com.qf.entity.Student" table="student">
<id name="sid" column="sid">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="sname" column="sname"/>
<!--
name:一的一方的对象的属性
class:一的一方的对象全路径
column:多的一方表的外键名称
-->
<many-to-one name="sch" class="com.qf.entity.School" column="s_sch"></many-to-one>
</class>
</hibernate-mapping>
配置核心配置文件hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url" >jdbc:mysql:///test02</property>
<property name="hibernate.connection.username" >root</property>
<property name="hibernate.connection.password" >root</property>
<property name="hibernate.dialect" >org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.hbm2ddl.auto" >update</property>
<property name="hibernate.show_sql" >true</property>
<property name="hibernate.format_sql" >true</property> <mapping resource="com/qf/entity/School.hbm.xml"/>
<mapping resource="com/qf/entity/Student.hbm.xml"/>
</session-factory>
</hibernate-configuration>
测试类
TestDemo.java
package com.qf.test; import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test; import com.qf.entity.School;
import com.qf.entity.Student;
import com.qf.util.SessionFactoryUtil; public class TestDemo {
@Test
public void test() {
Session session = SessionFactoryUtil.getSession();
Transaction tx = session.beginTransaction(); School sch1 = new School();
sch1.setSch_name("怀远一中");
School sch2 = new School();
sch2.setSch_name("包集中学"); Student stu1 = new Student();
stu1.setSname("张三");
Student stu2 = new Student();
stu2.setSname("李四");
Student stu3 = new Student();
stu3.setSname("王五"); stu1.setSch(sch1);
stu2.setSch(sch2);
stu3.setSch(sch1);
sch1.getStus().add(stu1);
sch1.getStus().add(stu3);
sch2.getStus().add(stu2); session.save(stu1);
session.save(stu2);
session.save(stu3);
session.save(sch1);
session.save(sch2); tx.commit();
}
}
查看数据库结果
student表
sid | sname | s_sch |
1 | 张三 | 1 |
2 | 李四 | 2 |
3 | 王五 | 1 |
school表
sch_id | sch_name | sch_address |
1 | 怀远一中 | |
2 | 包集中学 |
级联操作
级联,指的是操作一个对象的同时操作该对象相关联的对象
级联分类
- 级联保存或者更新
- 级联删除
级联保存或者更新
1. 操作一的一方
- 修改一的一方的映射文件
- School.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 配置表与实体的映射关系 -->
<class name="com.qf.entity.School" table="school">
<id name="sch_id" column="sch_id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="sch_name" column="sch_name"/>
<property name="sch_address" column="sch_address"/> <!-- name:多的一方的集合属性的名称 -->
<set name="stus" cascade="save-update">
<!-- column:多的一方表的外键名称-->
<key column="s_sch"></key>
<!-- class:多的一方的类的全路径 -->
<one-to-many class="com.qf.entity.Student"/>
</set>
</class>
</hibernate-mapping>
- School.hbm.xml
- 测试代码
- 测试方法
@Test
/**
*保存学校是否会保存学生信息
*保存的主体是学校,所以在学校的映射文件中添加级联配置
*<set name="stus" cascade="save-update">
*/
public void test() {
Session session = SessionFactoryUtil.getSession();
Transaction tx = session.beginTransaction(); School sch1 = new School();
sch1.setSch_name("包集中学"); Student stu1 = new Student();
stu1.setSname("李四"); stu1.setSch(sch1);
sch1.getStus().add(stu1); session.save(sch1); tx.commit();
}
- 测试方法
- 查看数据库中结果
-
student表
sid sname s_sch 1 李四 1 school表
sch_id sch_name sch_address 1 包集中学
-
2. 操作多的一方
- 操作多的一方的映射文件
- Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 配置表与实体的映射关系 -->
<class name="com.qf.entity.Student" table="student">
<id name="sid" column="sid">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="sname" column="sname"/>
<!--
name:一的一方的对象的属性
class:一的一方的对象全路径
column:多的一方表的外键名称
-->
<many-to-one cascade="save-update" name="sch" class="com.qf.entity.School" column="s_sch"></many-to-one>
</class>
</hibernate-mapping>
- Student.hbm.xml
- 测试代码
- 测试方法
@Test
/**
*保存学生是否会保存学校信息
*保存的主体是学生,所以在学生的映射文件中添加级联配置
*<many-to-one cascade="save-update" name="sch" class="com.qf.entity.School" column="s_sch"/>
*/
public void test() {
Session session = SessionFactoryUtil.getSession();
Transaction tx = session.beginTransaction(); School sch1 = new School();
sch1.setSch_name("怀远一中"); Student stu1 = new Student();
stu1.setSname("张三"); stu1.setSch(sch1);
sch1.getStus().add(stu1); session.save(stu1); tx.commit();
}
- 测试方法
- 查看数据库中结果
-
student表
sid sname s_sch 1 张三 1 school表
sch_id sch_name sch_address 1 怀远一中
-
3. 可以在多的一方和一的一方分别配置级联,这样操作一的一方、多的一方,同时都会操作另一方
@Test
/**
*保存学校是否会保存学生信息
*保存的主体是学校,所以在学校的映射文件中添加级联配置
*<set name="stus" cascade="save-update">
*/
public void test() {
Session session = SessionFactoryUtil.getSession();
Transaction tx = session.beginTransaction(); School sch1 = new School();
sch1.setSch_name("包集中学"); Student stu1 = new Student();
stu1.setSname("李四");
Student stu2 = new Student();
stu2.setSname("王五");
Student stu3 = new Student();
stu3.setSname("张三"); stu1.setSch(sch1);
sch1.getStus().add(stu2);
sch1.getStus().add(stu3); /*
* 学校插入一条记录,学生插入三条记录
* 因为保存stu1,级联保存sch1,保存sch1,级联保存stu2、stu3
*/
session.save(stu1);
/*
* 学生插入一条记录
* 因为保存stu2,stu2并没有其它关联的,所以只保存stu2
*/
//session.save(stu2);
/*
* 学校插入一条记录,学生插入两条记录
* 因为保存sch1,级联保存stu2、stu3
*/
//session.save(sch1); tx.commit();
}
级联删除
通常jdbc操作数据库情况下,如果要删除表中学校信息,直接删除学校是无法成功的,必须先将该学校下所有的学生信息删除完,才能删除学校信息
级联删除可以让我们在删除学校的同时级联删除相关的学生信息
1. 配置级联删除之前测试
@Test
public void delete() {
Session session = SessionFactoryUtil.getSession();
Transaction tx = session.beginTransaction(); /*
* 先查询再删除
*/
School school = session.get(School.class, 1L);
session.delete(school); tx.commit();
}
console输出
Hibernate:
select
school0_.sch_id as sch_id1_0_0_,
school0_.sch_name as sch_name2_0_0_,
school0_.sch_address as sch_addr3_0_0_
from
school school0_
where
school0_.sch_id=?
Hibernate:
update
student
set
s_sch=null
where
s_sch=?
Hibernate:
delete
from
school
where
sch_id=?
结论
- 删除学校信息时,默认先修改学生信息的外键(置为null),再删除学校信息
- hibernate删除一对多关系中一的一方某记录信息时,默认先将多的一方引用一的一方相关信息的外键置为null,再删除一的一方记录信息
2. 配置删除学校时级联删除学生
- 因为删除主体是学校,所以需要在学校的映射文件中做级联删除配置<set name="stus" cascade="delete">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 配置表与实体的映射关系 -->
<class name="com.qf.entity.School" table="school">
<id name="sch_id" column="sch_id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="sch_name" column="sch_name"/>
<property name="sch_address" column="sch_address"/> <!-- name:多的一方的集合属性的名称 -->
<set name="stus" cascade="delete">
<!-- column:多的一方表的外键名称-->
<key column="s_sch"></key>
<!-- class:多的一方的类的全路径 -->
<one-to-many class="com.qf.entity.Student"/>
</set>
</class>
</hibernate-mapping> 测试方法(必须先查询再删除,如果自己创建对象删除的话,自己创建的对象里信息可能不全,例如school中可能没有学生集合信息)
@Test
public void delete() {
Session session = SessionFactoryUtil.getSession();
Transaction tx = session.beginTransaction(); /*
* 先查询再删除
*/
School school = session.get(School.class, 1L);
session.delete(school); tx.commit();
}console输出
Hibernate:
select
school0_.sch_id as sch_id1_0_0_,
school0_.sch_name as sch_name2_0_0_,
school0_.sch_address as sch_addr3_0_0_
from
school school0_
where
school0_.sch_id=?
Hibernate:
select
stus0_.s_sch as s_sch3_1_0_,
stus0_.sid as sid1_1_0_,
stus0_.sid as sid1_1_1_,
stus0_.sname as sname2_1_1_,
stus0_.s_sch as s_sch3_1_1_
from
student stus0_
where
stus0_.s_sch=?
Hibernate:
update
student
set
s_sch=null
where
s_sch=?
Hibernate:
delete
from
student
where
sid=?
Hibernate:
delete
from
student
where
sid=?
Hibernate:
delete
from
student
where
sid=?
Hibernate:
delete
from
school
where
sch_id=?结论
- 删除学校信息时级联删除了相关学生信息
3. 删除学生信息级联删除学校信息(并不符合常理,一般不会使用)
- 配置学生映射文件
- <many-to-one name="sch" cascade="delete" class="com.qf.entity.School" column="s_sch"/>
维护关系inverse
inverse主要作用是指定哪一方来维护关系
- 取值是boolean,默认inverse="false"
- 如果一方的映射文件中设置为true,说明在映射关系中让另一方来维护关系;如果为false,就自己来维护关系
- 只能在一的一方设置
测试方法
2号学生本来是2号学校的,改变成1号学校
@Test
public void test() {
Session session = SessionFactoryUtil.getSession();
Transaction tx = session.beginTransaction(); Student stu = session.get(Student.class, 2L);
School sch = session.get(School.class, 1L); stu.setS_sch(2);
sch.getStus().add(stu); tx.commit();
}
1.不设置,使用默认值
School.hbm.xml
<set name="stus" cascade="save-update delete" inverse="false" >
<!-- column:多的一方表的外键名称-->
<key column="s_sch"></key>
<!-- class:多的一方的类的全路径 -->
<one-to-many class="com.qf.entity.Student"/>
</set>
Hibernate: select student0_.sid as sid1_1_0_, student0_.sname as sname2_1_0_, student0_.s_sch as s_sch3_1_0_ from student student0_ where student0_.sid=?
Hibernate: select school0_.sch_id as sch_id1_0_0_, school0_.sch_name as sch_name2_0_0_, school0_.sch_address as sch_addr3_0_0_ from school school0_ where school0_.sch_id=?
Hibernate: select stus0_.s_sch as s_sch3_1_0_, stus0_.sid as sid1_1_0_, stus0_.sid as sid1_1_1_, stus0_.sname as sname2_1_1_, stus0_.s_sch as s_sch3_1_1_ from student stus0_ where stus0_.s_sch=?
Hibernate: update student set sname=?, s_sch=? where sid=?
Hibernate: update student set s_sch=? where sid=?
2.设置另一方维护关系
School.hbm.xml
<set name="stus" cascade="save-update delete" inverse="true" >
<!-- column:多的一方表的外键名称-->
<key column="s_sch"></key>
<!-- class:多的一方的类的全路径 -->
<one-to-many class="com.qf.entity.Student"/>
</set>
Hibernate: select student0_.sid as sid1_1_0_, student0_.sname as sname2_1_0_, student0_.s_sch as s_sch3_1_0_ from student student0_ where student0_.sid=?
Hibernate: select school0_.sch_id as sch_id1_0_0_, school0_.sch_name as sch_name2_0_0_, school0_.sch_address as sch_addr3_0_0_ from school school0_ where school0_.sch_id=?
Hibernate: select stus0_.s_sch as s_sch3_1_0_, stus0_.sid as sid1_1_0_, stus0_.sid as sid1_1_1_, stus0_.sname as sname2_1_1_, stus0_.s_sch as s_sch3_1_1_ from student stus0_ where stus0_.s_sch=?
Hibernate: update student set sname=?, s_sch=? where sid=?
结论:
- 设置另一方维护关系时,少发送一次更新语句
- inverse="true"指的是由双向关联另一方维护该关联,己方不维护该关联
- Inverse默认为false,双向关系的两端都能控制,会造成一些问题(更新的时候会因为两端都控制关系,于是重复更新),可以在一端将inverse值设为true
最新文章
- 【原】Learning Spark (Python版) 学习笔记(四)----Spark Sreaming与MLlib机器学习
- phpstudy 80端口被占用,修改端口
- split拆分数组长度问题
- Gitlab备份、升级、恢复
- 【编程题目】n 个骰子的点数
- Nutch2.x 演示抓取第一个网站
- Java 时间转换问题总结
- MDM 证书申请流程(vendor及customer)
- VM 映像 PowerShell 教学系列博客文章
- codeforces 626E. Simple Skewness 三分
- c++ STL stack &; queue
- redis入门指南-第7章-管理
- struts 中的创建Action的三种方法
- SpringBoot 中 @RestController 和 @Controller 的区别
- 宇宙第一开发工具:vs2019 开发Python
- spring 之 property-placeholder 分析
- 基于ZigBee和STM32的智能家居控制系统的设计与实现(三)
- 第一个maven项目
- 【SIKIA计划】_05_Unity5.3开发2D游戏笔记
- django系列8.5--使用装饰器(视图函数中)实现用户登录状态检验