五、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

 1 package com.qf.entity;
 2 
 3 import java.util.HashSet;
 4 import java.util.Set;
 5 
 6 public class School {
 7     private long sch_id;
 8     private String sch_name;
 9     private String sch_address;
10     //一个学校有多个学生,应该是一对多关系中多的一方的集合,hibernate默认使用set集合
11     private Set<Student> stus = new HashSet<Student>();
12     
13     //get、set方法
14     public long getSch_id() {
15         return sch_id;
16     }
17     public void setSch_id(long sch_id) {
18         this.sch_id = sch_id;
19     }
20     public String getSch_name() {
21         return sch_name;
22     }
23     public void setSch_name(String sch_name) {
24         this.sch_name = sch_name;
25     }
26     public String getSch_address() {
27         return sch_address;
28     }
29     public void setSch_address(String sch_address) {
30         this.sch_address = sch_address;
31     }
32     public Set<Student> getStus() {
33         return stus;
34     }
35     public void setStus(Set<Student> stus) {
36         this.stus = stus;
37     }
38     
39     @Override
40     public String toString() {
41         return "School [sch_id=" + sch_id + ", sch_name=" + sch_name + ", sch_address=" + sch_address + ", stus=" + stus
42                 + "]";
43     }
44     public School() {
45         super();
46     }
47     
48 }
View Code

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

 1 package com.qf.entity;
 2 
 3 public class Student {
 4 
 5     private Long sid;
 6     private String sname;
 7     //一个学生只能有一个学校
 8     private School sch;
 9 
10     public Student() {
11         super();
12     }
13     
14     public Long getSid() {
15         return sid;
16     }
17     public void setSid(Long sid) {
18         this.sid = sid;
19     }
20     public String getSname() {
21         return sname;
22     }
23     public void setSname(String sname) {
24         this.sname = sname;
25     }
26     public School getSch() {
27         return sch;
28     }
29     public void setSch(School sch) {
30         this.sch = sch;
31     }
32     @Override
33     public String toString() {
34         return "Student [sid=" + sid + ", sname=" + sname + ", sch=" + sch + "]";
35     }
36     
37 }
View Code

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

 1 package com.qf.test;
 2 
 3 import org.hibernate.Session;
 4 import org.hibernate.Transaction;
 5 import org.junit.Test;
 6 
 7 import com.qf.entity.School;
 8 import com.qf.entity.Student;
 9 import com.qf.util.SessionFactoryUtil;
10 
11 public class TestDemo {
12     @Test
13     public void test() {
14         Session session = SessionFactoryUtil.getSession();
15         Transaction tx = session.beginTransaction();
16 
17         School sch1 = new School();
18         sch1.setSch_name("怀远一中");
19         School sch2 = new School();
20         sch2.setSch_name("包集中学");
21         
22         Student stu1 = new Student();
23         stu1.setSname("张三");
24         Student stu2 = new Student();
25         stu2.setSname("李四");
26         Student stu3 = new Student();
27         stu3.setSname("王五");
28 
29         stu1.setSch(sch1);
30         stu2.setSch(sch2);
31         stu3.setSch(sch1);
32         sch1.getStus().add(stu1);
33         sch1.getStus().add(stu3);
34         sch2.getStus().add(stu2);
35         
36         session.save(stu1);
37         session.save(stu2);
38         session.save(stu3);
39         session.save(sch1);
40         session.save(sch2);
41         
42         tx.commit();
43     }
44 }

查看数据库结果

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>
  • 测试代码
    • 测试方法
       1 @Test
       2 /**
       3  *保存学校是否会保存学生信息
       4  *保存的主体是学校,所以在学校的映射文件中添加级联配置
       5  *<set name="stus" cascade="save-update">
       6  */
       7 public void test() {
       8     Session session = SessionFactoryUtil.getSession();
       9     Transaction tx = session.beginTransaction();
      10 
      11     School sch1 = new School();
      12     sch1.setSch_name("包集中学");
      13     
      14     Student stu1 = new Student();
      15     stu1.setSname("李四");
      16 
      17     stu1.setSch(sch1);
      18     sch1.getStus().add(stu1);
      19     
      20     session.save(sch1);
      21     
      22     tx.commit();
      23 }
  • 查看数据库中结果  
    •   

      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. 可以在多的一方和一的一方分别配置级联,这样操作一的一方、多的一方,同时都会操作另一方

 1 @Test
 2 /**
 3  *保存学校是否会保存学生信息
 4  *保存的主体是学校,所以在学校的映射文件中添加级联配置
 5  *<set name="stus" cascade="save-update">
 6  */
 7 public void test() {
 8     Session session = SessionFactoryUtil.getSession();
 9     Transaction tx = session.beginTransaction();
10 
11     School sch1 = new School();
12     sch1.setSch_name("包集中学");
13     
14     Student stu1 = new Student();
15     stu1.setSname("李四");
16     Student stu2 = new Student();
17     stu2.setSname("王五");
18     Student stu3 = new Student();
19     stu3.setSname("张三");
20 
21     stu1.setSch(sch1);
22     sch1.getStus().add(stu2);
23     sch1.getStus().add(stu3);
24     
25     /*
26      * 学校插入一条记录,学生插入三条记录
27      * 因为保存stu1,级联保存sch1,保存sch1,级联保存stu2、stu3
28      */
29     session.save(stu1);
30     /*
31      * 学生插入一条记录
32      * 因为保存stu2,stu2并没有其它关联的,所以只保存stu2
33      */
34     //session.save(stu2);
35     /*
36      * 学校插入一条记录,学生插入两条记录
37      * 因为保存sch1,级联保存stu2、stu3
38      */
39     //session.save(sch1);
40     
41     tx.commit();
42 }

级联删除

通常jdbc操作数据库情况下,如果要删除表中学校信息,直接删除学校是无法成功的,必须先将该学校下所有的学生信息删除完,才能删除学校信息

级联删除可以让我们在删除学校的同时级联删除相关的学生信息

1. 配置级联删除之前测试

 1 @Test
 2 public void delete() {
 3     Session session = SessionFactoryUtil.getSession();
 4     Transaction tx = session.beginTransaction();
 5     
 6     /*
 7      * 先查询再删除
 8      */
 9     School school = session.get(School.class, 1L);
10     session.delete(school);
11     
12     tx.commit();
13 }

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
原文地址:https://www.cnblogs.com/qf123/p/10169990.html