数据库表与表之间的关系

一对多:一个学校可以有多个学生,一个学生只能有一个学校

多对多:一个学生可以有多个老师,一个老师可以教多个学生

一对一:一个人只能有一个身份证号,一个身份证号只能找到一个人

一对多关系

创建学生和学校表

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>
  • 测试代码
    • 测试方法

       @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>
  • 测试代码
    • 测试方法 

      @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主要作用是指定哪一方来维护关系

  1. 取值是boolean,默认inverse="false"
  2. 如果一方的映射文件中设置为true,说明在映射关系中让另一方来维护关系;如果为false,就自己来维护关系
  3. 只能在一的一方设置

测试方法

  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

最新文章

  1. 【原】Learning Spark (Python版) 学习笔记(四)----Spark Sreaming与MLlib机器学习
  2. phpstudy 80端口被占用,修改端口
  3. split拆分数组长度问题
  4. Gitlab备份、升级、恢复
  5. 【编程题目】n 个骰子的点数
  6. Nutch2.x 演示抓取第一个网站
  7. Java 时间转换问题总结
  8. MDM 证书申请流程(vendor及customer)
  9. VM 映像 PowerShell 教学系列博客文章
  10. codeforces 626E. Simple Skewness 三分
  11. c++ STL stack &amp; queue
  12. redis入门指南-第7章-管理
  13. struts 中的创建Action的三种方法
  14. SpringBoot 中 @RestController 和 @Controller 的区别
  15. 宇宙第一开发工具:vs2019 开发Python
  16. spring 之 property-placeholder 分析
  17. 基于ZigBee和STM32的智能家居控制系统的设计与实现(三)
  18. 第一个maven项目
  19. 【SIKIA计划】_05_Unity5.3开发2D游戏笔记
  20. django系列8.5--使用装饰器(视图函数中)实现用户登录状态检验

热门文章

  1. Centos上Docker的安装及加速
  2. 一、简单的图片上传并预览功能input[file]
  3. nginx部署静态资源
  4. 初学Java 九九乘法表
  5. Python3.5-20190526-廖老师-自我笔记-单元测试-参数换-paramunittest
  6. webRTC脱坑笔记(一)— 初识webRTC
  7. uwsgi部署django项目
  8. Oracle 包的学习
  9. inputAccessoryView,inputView
  10. .net core linux的守护进程 supervisor