数据库表与表之间的关系

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

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

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

一对多关系

创建学生和学校表

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

五、hibernate表与表之间的关系(一对多关系)的更多相关文章

  1. [转]NHibernate之旅(9):探索父子关系(一对多关系)

    本节内容 引入 NHibernate中的集合类型 建立父子关系 父子关联映射 结语 引入 通过前几篇文章的介绍,基本上了解了NHibernate,但是在NHibernate中映射关系是NHiberna ...

  2. SSAS中事实表中的数据如果因为一对多或多对多关系复制了多份,在维度上聚合的时候还是只算一份

    SSAS事实表中的数据,有时候会因为一对多或多对多关系发生复制变成多份,如下图所示: 图1 我们可以从上面图片中看到,在这个例子中,有三个事实表Fact_People_Money(此表用字段Money ...

  3. MySQL表与表的关系

    表与表的关系 一对多关系 ID name gender dep_name dep_desc 1 Chen male 教学部 教书育人 2 Old flying skin male 外交部 漂泊游荡 3 ...

  4. Laravel5.1 模型 --远层一对多关系

    远层一对多我们可以通过一个例子来充分的了解它: 每一篇文章都肯定有并且只有一个发布者 发布者可以有多篇文章,这是一个一对多的关系.一个发布者可以来自于一个国家 但是一个国家可以有多个发布者,这又是一个 ...

  5. 六、hibernate表与表之间的关系(多对多关系)

    多对多关系 创建实体类和对应映射文件 Student.java package com.qf.entity; import java.util.HashSet; import java.util.Se ...

  6. Hibernate学习笔记(四)—— 表与表的关系

    一.一对多|多对一 1.1 关系表达 1.1.1 表中的表达 建表原则:在多的一方创建外键指向一的一方的主键. 1.1.2 实体中的表达 [客户实体] public class Customer { ...

  7. Hibernate中表与表之间的关联多对多,级联保存,级联删除

    第一步:创建两个实体类:用户和角色实体类,多对多关系,并让两个实体类之间互相关联: 用户实体类: package com.yinfu.entity; import java.util.HashSet; ...

  8. Hibernate中表与表之间的关联一对多,级联保存和级联删除

    1:Hibernate的一对多操作(重点) 一对多映射配置 第一步:创建两个实体类:客户和联系人(例)以客户为一,联系人为多: package com.yinfu.entity; public cla ...

  9. mysql中相关,无关子查询,表与表之间的关系以及编码和乱码的解决

    ※MySQL中的字符编码(注意,utf8中没有'-',跟Java中不一样)SHOW VARIABLES; //查看系统变量//查询字符编码相关的系统变量SHOW VARIABLES WHERE var ...

随机推荐

  1. smbumount - 为普通用户卸载smb文件系统

    总览 smbumount 装载点 描述 普通用户使用这个程序可以卸载smb文件系统.它在工作时会suid到root身份,并且向普通linux用户提供了对资源更多的控制能力.在suid方面,它拥有足够的 ...

  2. smbspool - 将一个打印文件发送到一台SMB打印机

    总览 SYNOPSIS smbspool {job} {user} {title} {copies} {options} [filename] 描述 DESCRIPTION 此程序是Samba(7)套 ...

  3. vue,一路走来(1)--构建vue项目

    2016年12月--2017年5月,接触前端框架vue,一路走来,觉得有必要把遇到的问题记录下来. 那时,vux用的是1.0的vue,然而vue2.0已经出来了,于是我结合了mint-ui一起来做项目 ...

  4. (ACM模板)队列queue

    #include<iostream> #include<cstdio> #include<queue> using namespace std; struct po ...

  5. 《Spring Boot实战》笔记(目录)

    目录 目 录第一部分 点睛Spring 4.x第1 章 Spring 基础 .............................................................. ...

  6. Vue-列表渲染 非变异方法

    变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组.相比之下,也有非变异 (non-mutating method) 方法,例如:filter(), concat( ...

  7. 【串线篇】spring boot配置文件大全【下】

    一.配置文件占位符 1.1.随机数 ${random.value}.${random.int}.${random.long} ${random.int(10)}.${random.int[1024,6 ...

  8. LOJ3119. 「CTS2019 | CTSC2019」随机立方体 二项式反演

    题目传送门 https://loj.ac/problem/3119 现在 BZOJ 的管理员已经不干活了吗,CTS(C)2019 和 NOI2019 的题目到现在还没与传上去. 果然还是 LOJ 好. ...

  9. python关于window文件写入后,换行默认\r\n的问题

    因为python兼容各种平台,所以当在window打开文本文件写入后,换行会默认写成\r\n linux是\n 如果想去掉换行的\r 解决方法:在open函数里写入换行要求即可 with open(f ...

  10. 每天一个linux命令:less(14)

    less less命令的作用与more十分相似,都可以用来浏览文字档案的内容,less 在查看之前不会加载整个文件 .用less命令显示文件时,用PageUp键向上翻页,用PageDown键向下翻页. ...