五、hibernate表与表之间的关系(一对多关系)
数据库表与表之间的关系
一对多:一个学校可以有多个学生,一个学生只能有一个学校
多对多:一个学生可以有多个老师,一个老师可以教多个学生
一对一:一个人只能有一个身份证号,一个身份证号只能找到一个人
一对多关系
创建学生和学校表
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
五、hibernate表与表之间的关系(一对多关系)的更多相关文章
- [转]NHibernate之旅(9):探索父子关系(一对多关系)
本节内容 引入 NHibernate中的集合类型 建立父子关系 父子关联映射 结语 引入 通过前几篇文章的介绍,基本上了解了NHibernate,但是在NHibernate中映射关系是NHiberna ...
- SSAS中事实表中的数据如果因为一对多或多对多关系复制了多份,在维度上聚合的时候还是只算一份
SSAS事实表中的数据,有时候会因为一对多或多对多关系发生复制变成多份,如下图所示: 图1 我们可以从上面图片中看到,在这个例子中,有三个事实表Fact_People_Money(此表用字段Money ...
- MySQL表与表的关系
表与表的关系 一对多关系 ID name gender dep_name dep_desc 1 Chen male 教学部 教书育人 2 Old flying skin male 外交部 漂泊游荡 3 ...
- Laravel5.1 模型 --远层一对多关系
远层一对多我们可以通过一个例子来充分的了解它: 每一篇文章都肯定有并且只有一个发布者 发布者可以有多篇文章,这是一个一对多的关系.一个发布者可以来自于一个国家 但是一个国家可以有多个发布者,这又是一个 ...
- 六、hibernate表与表之间的关系(多对多关系)
多对多关系 创建实体类和对应映射文件 Student.java package com.qf.entity; import java.util.HashSet; import java.util.Se ...
- Hibernate学习笔记(四)—— 表与表的关系
一.一对多|多对一 1.1 关系表达 1.1.1 表中的表达 建表原则:在多的一方创建外键指向一的一方的主键. 1.1.2 实体中的表达 [客户实体] public class Customer { ...
- Hibernate中表与表之间的关联多对多,级联保存,级联删除
第一步:创建两个实体类:用户和角色实体类,多对多关系,并让两个实体类之间互相关联: 用户实体类: package com.yinfu.entity; import java.util.HashSet; ...
- Hibernate中表与表之间的关联一对多,级联保存和级联删除
1:Hibernate的一对多操作(重点) 一对多映射配置 第一步:创建两个实体类:客户和联系人(例)以客户为一,联系人为多: package com.yinfu.entity; public cla ...
- mysql中相关,无关子查询,表与表之间的关系以及编码和乱码的解决
※MySQL中的字符编码(注意,utf8中没有'-',跟Java中不一样)SHOW VARIABLES; //查看系统变量//查询字符编码相关的系统变量SHOW VARIABLES WHERE var ...
随机推荐
- 8VC Venture Cup 2017 - Elimination Round - A
题目链接:http://codeforces.com/contest/755/problem/A 题意:给定一个正整数N,问你是否存在一个数M使得N*M+1不是素数(M的范围在[1,1000]). 思 ...
- 初学Java if选择语句
import java.util.Scanner; public class SubtractionQuiz { public static void main(String[] agrs) { ); ...
- HTTP Error 500.30 - ANCM In-Process Start Failure
环境 windown 10 IIS 10 net core 2.2 vs2019 背景 在vs2019使用net core 2.2发布到IIS 10上(见在 ASP.NET Core 中使用多个环境) ...
- git基础常用命令
常用命令 git init //初始化本地git环境 git clone XXX//克隆一份代码到本地仓库 git pull //把远程库的代码更新到工作台 git pull --rebase ori ...
- maven之可执行jar包
在使idea创建springboot项目时,pom.xml文件中自动会添加下面这个插件. <build> <plugins> <plugin> <groupI ...
- springMVC带参数请求重定向
SpirngMVC返回逻辑视图名 可以分下面几种情况: 1. servlet进行请求转发,返回到jsp页面,如 return "index.jsp" ; 2. servlet 返 ...
- Apache Flink 的迁移之路,2 年处理效果提升 5 倍
一.背景与痛点 在 2017 年上半年以前,TalkingData 的 App Analytics 和 Game Analytics 两个产品,流式框架使用的是自研的 td-etl-framework ...
- 空间日志编辑器:word文档图文快速粘贴到web
百度ueditor可以实现word文档图文快速粘贴到web 1.4.2之后官方并没有做功能的改动,1.4.2在word复制这块没有bug,其他版本会出现手动无法转存的情况 本文使用的后台是Java.前 ...
- Word图片粘贴上传控件,直接粘贴图片到编辑器-DEDE
很多时候我们用一些管理系统的时候,发布新闻.公告等文字类信息时,希望能很快的将word里面的内容直接粘贴到富文本编辑器里面,然后发布出来.减少排版复杂的工作量. 下面是借用百度doc 来快速实现这个w ...
- Codeforces 814C - An impassioned circulation of affection
原题链接:http://codeforces.com/contest/814/problem/C 题意:有长度为n的一个字符串,q个询问,每个询问由数字m和字符c组成,问最多在字符串中替换m个字符,使 ...