Day40-Hibernate03(多表操作、配置多表关系、级联、放弃外键维护权)

今日内容:

通过案例学习hibernate的多表操作

1. 案例1:使用hibernate完成1对多的关联关系映射并操作
    * 部门和员工。1对多的关系,一个部门有多名员工;一名员工,只会归属某一个部门。

2. 案例2:使用hibernate完成多对多的关联关系映射并操作
    * 学生选课。学生表和课程表之间,就是多对多关系。一个学生,可以选择多门课程。一门课程,可以被多个学生选择。

java表达表与表的关系 – 表达1对多关系(部门和员工)

1)数据库的表达:利用外简约束表示表与表之间的关系

create database day39_hibernate;
use database day39_hibernate;
CREATE TABLE department(
      did INT PRIMARY KEY AUTO_INCREMENT,
      dname CHAR(20));
CREATE TABLE employee(
      eid INT PRIMARY KEY AUTO_INCREMENT,
      ename CHAR(20),
      e_did INT,
      FOREIGN KEY (e_did) REFERENCES department(did)
      );

2)java的表达–javaBean中表达部门和员工对象之间的关系:
在javaBean中创建私有字段中创建相应的对象表达与某种对象之间的关系

        // 每个部门,有多个员工
        class Department{
            Integer  id;
            String   name;
            // 这个部门对应的所有员工
            Set<Employee> employees=new HashSet<Employee>();
         }

        // 每个员工归属于一个部门
        class Employee{
            Integer  id;
            String   name;
            //Integer department_id;  // 部门的id,不够面向对象
            Department department; // 员工所在的部门
         }
          //上述方式双向1对多
 **为什么当部门对象中含有多个员工的时候,选择在部门对象中设定一个set集合用来表示该部门的员工:**
  1) 第一个知识点:set,list, map的区别(必须记住的)

*              list: 有序(记住了插入的顺序),可以重复的
*              set:无序(不记住插入的顺序),不可以重复
*              map: 保存的键值对(key,value), 其中key,不能重复。

 2)hibernate使用set的原因:(hibernate推荐使用set)
      set特点是,无序(符合数据库的特性),不重复(可以去掉重复的数据)。
      set的主要方法就使用:add(), remove()

Java表达存在方向性:
1对多为例。存在三种情况: 单向1对多,单向多对1, 双向1对多
数据库表达方面,都是一样的,都是多的一方使用外键指向一的一方的主键
但是java的表达存在不同:

a)单向1对多
可以从1的一方(部门)找到多的一方(员工),反过来不可以。

               // 每个部门,有多个员工
                class Department{
                    Integer  id;
                    String   name;
                    Set<Employee> employees=new HashSet<Employee>();
                 }

                class Employee{
                    Integer  id;
                    String   name;
                 }

b) 单向多对1
可以从多的一方(员工)找到的1的一方(部门),反过来不可以。

              class Department{
                    Integer  id;
                    String   name;
                 }

                // 每个员工归属于一个部门
                class Employee{
                    Integer  id;
                    String   name;
                    Department department;
                 }

c)双向1对多:
即可以从多找到1,又可以从1找到多。

               // 每个部门,有多个员工
                class Department{
                    Integer  id;
                    String   name;
                    Set<Employee> employees=new HashSet<Employee>();
                 }

                // 每个员工归属于一个部门
                class Employee{
                    Integer  id;
                    String   name;
                    Department department;
                 }

方向性有什么价值啊?
* 考虑场景:实际开发的时候。解决现实中存在的问题。
* 部门和员工。
* 单向多对1,更加实用。

3)映射配置文件的编写
1的一方 —- 部门

    <set name="employees" cascade="save-update" inverse="true">
        <key column="e_did"></key>
        <one-to-many  class="cn.itcast.domain.Employee"/>
    </set>

配置标签讲解:
1的一方hbm.xml映射–set标签的理解(重点)

*     set标签:javaBean中是set集合

        * name: 多的一方,在1的一方对应类中属性的名字


*     key标签:查询的关键。知道了部门信息,查询员工的语句:

        参考:select * from employee where fk_did =?
        * column: 多的一方对应的表中,指向1的一方外键的名称 ,这个是在数据库表中的列名


*     one-to-many标签: 1对多

        * class: 查询到的结果,要封装的java类型。即多的一方对应类的全路径名称

双向1对多的配置:
多的一方:

<many-to-one name="department" column="e_did"
    class="com.itheima.domain.Department" cascade="save-update"></many-to-one>

多的一方映射文件–manytoone标签的理解

    many-to-one标签:从多的一方查询1的一方
        * name: 一的一方,在多的一方的持久化类中,属性的名字
        * class: 查询到的结果,要封装的java类型。即 一的一方对应的类的全路径名称
        * column: 多的一方,对应的表中,外键所在字段的名称。思考查询: 已知员的信息,去查询部门的信息。

hibernate的级联:

引申:经典错误:object references an unsaved transient instance

     分析:事务提交时,持久化态的对象关联了瞬时态的对象,就会报上述错误。(没配置级联时)
     解决方案:配置级联保存
         * 级联重要的是理解:方向性

级联的定义:
a. 当主控方执行保存、更新或者删除操作时,其关联对象(被控方)也执行相同的操作。
b. 在映射文件中通过对cascade属性的设定来控制是否对关联对象,采用级联操作。
c. 举例:在部门和员工的关系中,在部门对应的映射文件上,配置了级联保存,保存部门的同时,自动把部门关联的员工,也保存了。

配置:
a. 映射文件中,和都可以配置级联属性:cascade
b. 级联的常见取值

    * 
        *         none:        默认值。所有情况下均不进行关联操作
        *         save-update: 在执行save/update/saveOrUpdate时进行关联操作。
        *         delete:      在执行delete时进行关联操作
        *         all:         所有情况下均进行关联操作。
c. 使用举例:`cascade="save-update"`

重点:理解方向性(级联操作和关系的方向性)


案例:

      @Test
      // 小游戏
      public void test3(){

           Session session =HibernateUtils.openSession();
           Transaction transaction=session.beginTransaction();


           // 参考代码--保存操作:1个部门3个员工
           Department department = new Department();
           department.setName("研发部");

           Employee employee1 = new Employee();
           employee1.setName("研发1号");
           Employee employee2 = new Employee();
           employee2.setName("研发2号");
           Employee employee3 = new Employee();
           employee3.setName("研发3号");

           // 建立双向关系
           department.getEmployees().add(employee1);
           employee1.setDepartment(department);

           employee2.setDepartment(department);
           employee3.setDepartment(department);

           // 双方配置级联保存
           // 执行保存操作,单独执行语句1,2,3,4,问分别数据库分别保存了几条数据,那些数据
           // 语句1
           //session.save(department);// 部门,1号员工

           // 语句2
           //session.save(employee1);  // 1, 部门,

           // 语句3
           //session.save(employee2); //2, 部门,1号

           // 语句4
           session.save(employee3); //3, 部门,1号

           // 释放资源
           transaction.commit();
           session.close();

关于级联删除:

a. 级联删除的配置

    * 绝对不会使用 ,配置方法就是在映射配置文件中写上:cascade="delete"

b. 演示(同样存在方向性)
(1) 不配置级联删,默认情况下的删除操作
(2) 只删除部门
(3) 只删除员工

c. 恐怖的双向级联删除
* 如双方都配置了级联删除。
* 删除一个员工,先删除这个员工,然后通过“员工的级联删除”配置,把对应的部门删除,再次通过“部门的级联删除”配置,把这个部门其他的员工全部删除。

d. 级联删除的鸡肋性
* 回顾商品的上下架问题。与其直接删除商品,不如为商品设置一个属性:上下架属性,将其下架即可,这样仍然可以查询到商品的相关信息,也不会引起数据库的问题


对象导航查询:

对象导航查询
* 体会hibernate之美,自动查询了关联对象

      // 对象导航查询
      @Test
      public void test02() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Department department = session.get(Department.class, 3);
           System.out.println(department.toString());
            System.out.println(department.getEmployees().toString());

           // 释放资源
           transaction.commit();
           session.close();
      }

放弃外键维护权:

原因:双向关系的时候,更新数据的时候,会导致重复sql语句查询和修改数据库,增加数据库的访问量

a. 演示双向关系导致的重复sql语句
(1) 模拟:员工更换部门
(2) 原因分析:hibernate默认部门和员工双方,都维护外键,从而导致多余的sql语句。
(3) 解决方案:其中一方放弃外键维护权

b. 放弃外键维护权
* 重点:所谓放弃外键维护权:指不负责维护双方的关系。
配置方式:
在set或者many-to-one标签上,配置inverse属性。解释:逆转。默认值是:false,不放弃;设置成true,表示放弃外键维护权。
Hibernate推荐:
1对多的关系:应该由多的这方维护。1的一方,放弃外键维护,所以在1 的这方的set属性里面配置放弃外键

重点理解:cascade和inverse的区别
a. 侧重点不同
(1) cascade:级联,强调操作一个对象的时候,是否操作其关联对象
(2) inverse:强调的是外键的维护权

案例:
* 保存操作,一个部门,2个员工。部门配置了级联保存,并按照规范,放弃了外键维护权。维护了部门到员工的单向关系,只保存部门。

public void test04() {
        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();
        // 节省时间的代码
        Department department =new Department();
        department.setName("供应链部");

        Employee employee1 =new Employee();
        employee1.setName("张三1");

        Employee employee2 =new Employee();
        employee2.setName("张三2");

        // 建立关系
        department.getEmployees().add(employee1);
        department.getEmployees().add(employee2);

        // 保存
        session.save(department);

        // 释放资源
        transaction.commit();
        session.close();
      }
c. 分析(重点)
    (1) 前提:部门上配置了级联保存和放弃外键维护权,部门单向关联员工
    (2) 保存部门,出现的现象是:部门的数据保存了,员工的数据保存了,但是员工记录中的外键是null.
    (3) 放弃外键维护权本质:放弃对双方关系的表达。
        * 在一对多中,通过外键字段体现关系,放弃外键维护权,即不负责维护外键字段。

hibernate:多对多 —- 学生选课

1)数据库准备:
数据库表表示两张表的多对多关系方式:

create database day39_hibernate;
user database day39_hibernate;
CREATE TABLE student(
      sid INT PRIMARY KEY AUTO_INCREMENT,
      sname CHAR(20)
);
CREATE TABLE course(
      cid INT PRIMARY KEY AUTO_INCREMENT,
      cname CHAR(20)
);
CREATE TABLE student_course(
      id INT PRIMARY KEY AUTO_INCREMENT,
      fk_sid INT,
      fk_cid INT,
      FOREIGN KEY (fk_sid) REFERENCES student(sid),
      FOREIGN KEY (fk_cid) REFERENCES course(cid)
);

2)java表达:
student类:

public  class Student {

      private Integer id;
      private String name;
      private Set<Course> courses = new HashSet<Course>();
}

Course类:

// 持久化的类
public  class Course {

      private Integer id;  //oid
      private String name;
      private Set<Student> students=new HashSet<Student>();
}

3)映射配置文件的写法:
course .hbm.xml

 <set name="students" table="student_course" cascade="save-update">
     <key column="fk_cid"></key>
     <many-to-many class="com.itheima.domain.Student" column="fk_sid"></many-to-many>
 </set>

student.hbm.xml

<set name="courses" table="student_course" cascade="save-update">
    <key column="fk_sid"></key>
    <many-to-many class="com.itheima.domain.Course" column="fk_cid"></many-to-many>
</set>
*     set标签:javaBean是set集合

    * name: 关联的多的一方,在持久化类中属性的名字
    * table: 中间表的名字


*     key标签:查询的关键。知道了课程的信息,查询选修了这门课程的所有学生

         参考: select * from student_course where sc_cid =?
    * column: 自身在中间表中对应的外键的字段名称


*     many-to-many标签: 多对多

    * class: 多对多的查询,查询到的结果,封装的类型的全路径名称
    * column :关联的多的一方,在中间表中,外键字段的名称

多对多案例:

    @Test
      public void test01() {
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Student student1 = new Student();
           student1.setName("刘德华");
           Student student2 = new Student();
           student2.setName("郭富城");

           Course course1 = new Course();
           course1.setName("java");
           Course course2 = new Course();
           course2.setName("python");
           Course course3 = new Course();
           course3.setName("c++");

           // 2. 建立关系(学生选课)
           // 学生到课程
           // 在多对多的关系中,每个关系,对应中间表的一条记录
           // 放弃外键维护权:不负责处理关系。每个关系,对应中间表的一条记录
           // 在1对多的关系中,每个关系,只是对应一个外键字段。
           student1.getCourses().add(course1);
           student1.getCourses().add(course2);
           student2.getCourses().add(course1);
           student2.getCourses().add(course3);

           // 3. 保存操作
           session.save(student1);
           session.save(student2);

           // 释放资源
           transaction.commit();
           session.close();
      }

多对多的放弃外键维护权
a. hibernate默认,学生和课程双方,都维护外键
b. 上面提到的,维护外键的本质是:表达双方的关系。
c. 多对多中,表达双方的关系,不是通过某个外键字段,而是通过中间表的一条完整的记录。即:这里的外键,其实指中间表的一条记录。
d. 不同于1对多,多对多中,增加一个关系,中间表增加一条记录;减少一个关系,中间表,删除一个记录来实现。

放弃外键维护权
a. 在many-to-many标签上,配置inverse属性。设置成:true,放弃外键维护权
b. 多对多的关系:主动方维护外键,被动方,放弃外键维护权。
* 理解主动与被动:学生选课。学生是主动方。

案例:操作中间表

      @Test
      public void test04() { //新增一门课程
           Session session = HibernateUtils.openSession();
           Transaction transaction = session.beginTransaction();

           Student student = session.get(Student.class, 10);
           Course course = session.get(Course.class, 14);

           student.getCourses().add(course);

           // 释放资源
           transaction.commit();
           session.close();
      }

      @Test
      // 学生新增一门课程
      // 郭富城,觉得移动互联网是未来的趋势,python更加适合自己。c++暂时先不学,学习python
      public void test3(){

           Session session =HibernateUtils.openSession();
           Transaction transaction=session.beginTransaction();


           Student student1=session.get(Student.class, 4);// 郭富城
           Course course1 =session.get(Course.class, 6);// c++
           Course course2 =session.get(Course.class, 4);// python

           student1.getCourses().remove(course1);
           student1.getCourses().add(course2);

           // 释放资源
           transaction.commit();
           session.close();

      }

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值