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

来源:互联网 发布:数据分析入门 编辑:程序博客网 时间:2024/06/02 04:35

今日内容:

通过案例学习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();      }

阅读全文
0 1
原创粉丝点击