• 五、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
  • 相关阅读:
    简明python教程五----数据结构
    简明python教程四-----模块
    简明python教程三-----函数
    简明python教程二-----对象
    linux命令行与shell脚本编程 -----15控制脚本
    14.7创建临时文件
    js --- return返回值 闭包
    js --- 事件流
    git --- ! [rejected] master -> master (non-fast-forward)
    html --- bootstrap 框架 (栅格系统布局)
  • 原文地址:https://www.cnblogs.com/qf123/p/10169990.html
Copyright © 2020-2023  润新知