前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >day30_Hibernate学习笔记_02

day30_Hibernate学习笔记_02

作者头像
黑泽君
发布2018-10-11 10:42:54
9950
发布2018-10-11 10:42:54
举报
文章被收录于专栏:黑泽君的专栏

一、Hibernate中对象的状态

对象状态 瞬时态|临时态:Transient     1、没有与Hibernate产生关联(没有与Session有关联)。     2、与数据库中的记录没有产生关联,即对象没有ID(有关联:就是与数据库中的ID有对应)。 持久态:Persistent     1、与Hibernate有关联(与session有关联)。     2、对象有ID。 游离态|脱管态:Detached     1、没有与Hibernate产生关联。     2、对象有ID。 三种状态转换如下图所示:

示例代码如下:

代码语言:javascript
复制
package com.itheima.a_state;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;

// 对象的三种状态
public class Demo1 {
    @Test
    // 演示对象三种状态
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u = new User();    // 瞬时状态
        u.setName("tom");       // 瞬时状态
        u.setPassword("1234");  // 瞬时状态

        session.save(u);        // 持久状态
        // 问题: 调用完save方法后,数据库中有没有对应记录?
        // 答:数据库中没有对应记录,但是最终(未来)会被同步到数据库中,所以仍然是持久状态。

        session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
        session.close();        // 游离状态
    }

    @Test
    // 三种状态的转换
    // 瞬时  => 持久
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u = new User();    // 瞬时状态
        u.setName("tom");       // 瞬时状态
        u.setPassword("1234");  // 瞬时状态

        session.save(u);        // 持久状态 
        // save方法会使用主键生成策略,只是为User对象指定id =>
        // native    => 主键自增,会打印 insert into 语句
        // increment => 数据库自己生成主键,先从数据库中查询最大的ID值,将ID值加1作为新的主键,不建议使用,存在线程并发问题
        // assigned  => 需要手动指定主键,不手动指定将会报错

        session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
        session.close();        // 游离状态
    }

    // 瞬时  => 游离
    // 瞬时: 没有关联,没有id
    // 游离: 没有关联,有id(与数据库中对应的id)
    @Test
    public void fun3() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u = new User();    // 瞬时状态
        u.setId(1);             // 游离状态

        session.getTransaction().commit(); // 游离状态
        session.close(); // 游离状态        
    }

    @Test
    // 持久  => 瞬时
    // 持久: 有关联,有id
    // 瞬时: 无关联,无id
    public void fun4() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 通过get方法,得到持久状态对象
        User u = (User) session.get(User.class, 1); // 持久状态

        session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
        session.close();    // 游离状态

        u.setId(null);      // 瞬时状态
    }

    @Test
    // 持久  => 瞬时
    // 持久: 有关联,有id
    // 瞬时: 无关联,无id
    public void fun5() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 通过get方法,得到持久状态对象
        User u = (User) session.get(User.class, 1); // 持久状态
        session.evict(u);   // 游离状态,该方法将User对象与session的关联移除
        u.setId(null);      // 瞬时状态

        session.save(u);    // 持久状态

        session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
        session.close();    // 游离状态
    }

    @Test
    // 持久  => 游离
    // 只需要将session的关联取消
    public void fun6() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 通过get方法,得到持久状态对象
        User u = (User) session.get(User.class, 1); // 持久状态
        session.evict(u);   // 游离状态

        session.getTransaction().commit(); // 游离状态
        session.close();    // 游离状态
    }

    @Test
    // 游离  => 瞬时
    // 移除ID
    public void fun7() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 通过get方法,得到持久状态对象
        User u = (User) session.get(User.class, 1); // 持久状态
        session.evict(u);   // 游离状态
        u.setId(null);      // 瞬时状态

        session.getTransaction().commit(); // 瞬时状态
        session.close(); // 瞬时状态
    }

    @Test
    // 游离  => 持久
    // 是否与Session关联
    public void fun8() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 通过get方法,得到持久状态对象
        User u = (User) session.get(User.class, 1); // 持久状态
        session.evict(u);   // 游离状态
        session.update(u);  // 持久状态

        session.getTransaction().commit(); // 持久状态 ,在事务提交时,会把持久化状态对象同步(更新)到数据库中
        session.close();    // 瞬时状态
    }

    // 三种状态有什么用?
    //  答: 持久状态,我们使用Hibernate主要是为了持久化我们的数据。
    //       对于对象的状态,我们期望我们需要同步到数据库的数据,都被转换成持久状态。
    //       持久化状态的特点:Hibernate会自动将持久化状态对象的变化同步到数据库中。
    @Test
    public void fun9() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 通过get方法,得到持久状态对象
        User u = (User) session.get(User.class, 1); // 持久状态

        // u.setName("jerry");  // 依然是持久状态
        u.setId(3);             // 会报错。因为Hibernate中规定:与session建立关联的对象的ID,不允许修改!

        session.update(u);      // 多余代码  => 因为Hibernate会自动将持久化状态对象的变化同步(更新)到数据库中。

        session.getTransaction().commit(); // 持久状态  => 打印update语句
        session.close();        // 瞬时状态
    }
}

二、一级缓存

  一级缓存(更深层次理解Hibernate中对象的操作)   缓存:Hibernate中也存在缓存。Hibernate中存在的缓存也是用来提高效率的。 Hibernate中存在两种缓存1、线程级别的缓存。也叫Session缓存、Hibernate一级缓存(今天学) 2、进程级别的缓存。也叫Hibernate二级缓存(最后一天学) Session缓存:就是Session对象中存在的缓存,缓存中存在的是(持久化)对象。

一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。

2.1、Session缓存的细节问题

示例代码如下:

代码语言:javascript
复制
package com.itheima.b_cache;

import java.util.List;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;

// session缓存 的细节问题
public class Demo2 {
    @Test
    // 1、
    //  保存对象时使用 save方法
    //  保存对象时使用 persist方法
    //  有区别吗?答:没有区别,名称不同,功能一样。
    //      persist(持久)方法 来自于JPA接口
    //      save(保存)方法来自于Hibernate
    public void fun1() {
        Session session = HibernateUtils.openSession();
        // session.beginTransaction();

        User u = new User();
        u.setName("张三");

        // session.save(u); // insert into 语句被打印  => 目的:获得id
        session.persist(u);

        // session.getTransaction().commit();
        session.close(); 
    }

    // 2.1、HQL语句查询是否会使用一级缓存?答:HQL语句查询不会使用一级缓存。
    @Test
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        List<User> list1 = session.createQuery("from User").list(); // 会打印一条select语句

        List<User> list2 = session.createQuery("from User").list(); // 又会打印一条select语句

        List<User> list3 = session.createQuery("from User").list(); // 又又会打印一条select语句

        session.getTransaction().commit();
        session.close(); 
    }

    // 2.2、HQL语句批量查询时,查询结果是否会进入一级缓存?答:HQL语句查询的结果会放入一级缓存中。
    @Test
    public void fun3() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        List<User> list1 = session.createQuery("from User").list(); // 会打印一条select语句

        User u = (User) session.get(User.class, 1); // 不会再打印一条select语句了,因为使用了一级缓存

        session.getTransaction().commit();
        session.close(); 
    }

    @Test
    // 3.1、原生SQL语句查询的结果会不会放入一级缓存中?答:如果把查询结果封装到对象中,对象会放入一级缓存中。
    public void fun4() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        List<User> list1 = session.createSQLQuery("select * from t_user").addEntity(User.class).list(); // 会打印一条select语句

        User u = (User) session.get(User.class, 1); // 不会再打印一条select语句了,因为使用了一级缓存

        System.out.println(u);

        session.getTransaction().commit();
        session.close(); 
    }

    @Test
    // 3.2、原生SQL语句查询的结果会不会放入一级缓存中?答:如果没有把查询结果封装到对象中,对象不会放入一级缓存中。
    public void fun5() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        List list1 = session.createSQLQuery("select * from t_user").list();  // 会打印一条select语句

        User u = (User) session.get(User.class, 1);  // 又会打印一条select语句

        System.out.println(u);

        session.getTransaction().commit();
        session.close();
    }

    // 同理:Hibernate中的criteria查询  => 也会将查询结果放入一级缓存. 但是查询不会使用一级缓存,与HQL查询结论一致。
}

2.2、一级缓存快照【掌握】

快照:与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与一级缓存的数据必须一致。   如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。   示例代码如下:

代码语言:javascript
复制
package com.itheima.b_cache;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;

// session缓存
public class Demo1 {
    @Test
    // 证明session缓存的存在
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 把持久化状态的对象  => 存到缓存中
        User u1 = (User) session.get(User.class, 1); // 发送select语句,从数据库取出记录,并封装成对象

        User u2 = (User) session.get(User.class, 1); // 再次查询时,会从缓存中查找,不会再发送select

        User u3 = (User) session.get(User.class, 1); // 再次查询时,会从缓存中查找,不会再发送select

        System.out.println(u1 == u2); // true
        System.out.println(u1 == u3); // true

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // session缓存中的快照
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = (User) session.get(User.class, 1); // 持久状态,会打印select

        session.update(u1);     // 持久状态,不会打印select语句

        session.getTransaction().commit(); // 不会打印select语句
        session.close(); 
    }

    @Test
    // session缓存中的快照
    public void fun3() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = new User();   // 瞬时状态
        u1.setId(1);            // 游离状态
        u1.setName("jerry");    // 游离状态
        u1.setPassword("1234"); // 游离状态

        session.update(u1);     // 持久状态,不会打印select语句

        session.getTransaction().commit(); // 会打印select语句
        session.close();
    }

    @Test
    // 感受一级缓存效率的提高
    public void fun4() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = (User) session.get(User.class, 1); // 持久化状态

        u1.setName("tom");
        session.update(u1);
        u1.setName("jack");
        session.update(u1);
        u1.setName("rose");
        session.update(u1); // 持久化状态: 本质就是存在缓存中的对象。

        session.getTransaction().commit();
        session.close(); 
    }
}

一级缓存中对象和快照图解如下图所示:

2.3、关于学习Session缓存的几个问题

示例代码如下_01:

代码语言:javascript
复制
package com.itheima.c_question;

import java.util.List;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;

// 学生问题
// 1、save方法 和 persist方法 对比之后,没有区别?
public class Demo1 {
    @Test
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u = new User();

        u.setId(99);

        session.persist(u); // 体现的是持久化,persist提供的理念是将对象完整的持久化,持久化也包括对象的ID。
                            // 如果在持久化之前设置了ID,那么就会将设置的ID进行insert,但是主键策略是由数据库来维护的。所以会产生矛盾,抛出异常。

        session.getTransaction().commit();
        session.close();
    }

    @Test
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u = new User();

        u.setId(99);

        session.save(u); // save方法,如果保存的对象在保存之前设置了ID,那么该ID也被认为是无效的ID。

        System.out.println(u.getId()); // ID是根据主键策略自动生成的,不是99

        session.getTransaction().commit();
        session.close();
    }
}

示例代码如下_02:

代码语言:javascript
复制
package com.itheima.c_question;

import java.util.List;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;

// 学生问题
// 2、Hql查询,查询的结果会放入Session一级缓存中,但是为什么每次调用Hql查询都会生成Sql语句呢?
// 并不代表 Hql没有使用一级缓存。
public class Demo2 {
    @Test
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        List<User> list1 = session.createQuery("from User").list(); // 发送sql
        List<User> list2 = session.createQuery("from User").list(); // 发送sql

        System.out.println(list1.get(0) == list2.get(0)); // true =>

        session.getTransaction().commit();
        session.close();
    }

// 3、缓存中的数据如果与数据库中的数据不同步,会怎么样?
//     答:会优先使用缓存中的。
//    如何解决不同步问题呢?
//     答:使用JDBC

//    在一级缓存中出现该问题的几率比较小。因为一级缓存的生命周期比较短。一级缓存的生命周期类似于request对象。
//        openSession();    => 一级缓存生命周期开始
//        session.close();  => 一级缓存销毁
    @Test
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = (User) session.get(User.class, 1);
        User u2 = (User) session.get(User.class, 1);

        session.getTransaction().commit();
        session.close();
    }
}

createQuery语句查询图解如下:

2.4、Session其他API(大部分都是了解即可)

示例代码如下:

代码语言:javascript
复制
package com.itheima.d_api;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;

// 其他API(大部分都是了解即可)
public class Demo1 {
    @Test
    // 1、evict(); 将缓存中的对象移除
    // 2、clear(); 清空一级缓存
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = (User) session.get(User.class, 1);
        session.clear(); // 清空一级缓存
        User u2 = (User) session.get(User.class, 1);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // 3、refresh(Object); 刷新 => 强制刷新缓存中的对象 => (可以用来解决缓存与数据库数据不同步的问题(局部解决))
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = (User) session.get(User.class, 1);
        session.refresh(u1); // 将缓存中的对象立刻与数据库同步,会再发送一个sql语句

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // 4、flush(); 对比快照,并提交缓存对象
    public void fun3() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = (User) session.get(User.class, 1);
        // u1.setName("zhangsan"); // 使得缓存和快照不一致
        session.flush(); // 立刻提交session缓存中的对象到数据库

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // 代理主键   => native
    // 5.1、saveOrUpdate(Object); 方法
    // saveOrUpdate 可以同时完成保存或更新操作
    //      主键为空 时   =>  执行save语句
    //      主键有值 时   =>  执行update语句
    public void fun4() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u = new User();
        u.setId(99);
        u.setName("jack");
        u.setPassword("1234");

        session.saveOrUpdate(u);

        session.getTransaction().commit();
        session.close();
    }

    // 自然主键   => assigned
    // 5.2、update 与 saveOrUpdate方法
    // saveOrUpdate 可以同时完成保存或更新操作
    //      主键为空时   =>  报错,因为无论是save还是update,都必须指定id
    //      主键有值时   =>  先会根据主键查询数据库,看数据库是什么情况:
    //          数据库中存在这条数据时      =>  执行update语句
    //          数据库中不存在这条数据时  =>  执行insert语句
    @Test
    public void fun5() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u = new User();
        u.setId(88);
        u.setName("jack01");
        u.setPassword("1234");

        session.saveOrUpdate(u);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // 在我们使用Hibernate的时候,注意避免出现:两个相同的ID对象,放入一级缓存的情况。因为一级缓存本质上是一个Map结合。键是ID不能相同。
    public void fun6() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        User u1 = (User) session.get(User.class, 1);  // 持久状态,缓存中存在
        session.evict(u1); // 游离状态,缓存中不存在
        User u2 = (User) session.get(User.class, 1);  // 持久状态,缓存中存在,id一样,但是对象不是同一个

        session.update(u1); // 将u1重新转换为持久状态,缓存中存在,报错
        session.getTransaction().commit();
        session.close();
    }
}

三、多表设计

  • 在开发中,前期进行需求分析,需求分析提供ER图(实体关系图),根据ER图编写表结构。
  • 表之间关系存在3种:一对多、多对多、一对一。(回顾)

一对多:1表(主表)必须主键和多表(从表)必须外键,主表主键与从表外键形成主外键关系。 多对多:提供中间表(从表),提供2个字段(外键)分别对应两个主表。 一对一:非常少见。

  • 如何使用面向对象的思想通过代码描述对象与对象之间关系?如下所示:
代码语言:javascript
复制
----------------------------------
一对多:客户Customer和订单Order
private class Customer {
    // 一对多:一个客户拥有多个订单
    private Set<Order> orderSet;
  ......
}
private class Order {
    // 多对一:多个订单属于一个客户
    private Customer customer;
  ......
}
----------------------------------
多对多:Student学生和Course课程
private class Student {
    // 多对多:多个学生学习不同课程
    private Set<Course> courseSet;
  ......  
}
private class Course {
    // 多对多:多个课程被不同学生学习
    private Set<Student> student;
  ......
}
----------------------------------
一对一:公司Company 和地址Address
private class Company {
    private Address address;
  ......
}
private class Address {
    private Company company;
  ......
}
----------------------------------

四、Hibernate的关联关系映射(一对多)

4.1、一对多实现【掌握】

4.1.1、实现类

Customer.java

代码语言:javascript
复制
package com.itheima.domain;

import java.util.HashSet;
import java.util.Set;

public class Customer {
    private Integer cid;
    private String cname;
    // 一对多:一个客户拥有多个订单
    // 在一的一方,使用集合表达持有多的一方引用,建议使用Set(不重复、无序)
    private Set<Order> orders = new HashSet<Order>(); // 建议实例化,使用方便

    // getter和setter方法
}

Order.java

代码语言:javascript
复制
package com.itheima.domain;

public class Order {
    private Integer oid;
    private String oname;
    // 多对一:多个订单 属于 一个客户
    private Customer customer;

    // getter和setter方法
}
4.1.2、配置文件

Customer.hbm.xml

代码语言:javascript
复制
<?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  package="com.itheima.domain">
    <class name="Customer" table="t_customer">
        <id name="cid" column="cid">
            <generator class="native"></generator>
        </id>
        <property name="cname" column="cname" type="String"></property>

        <!-- 一对多:一个客户当前客户 拥有 多个订单
            1 、确定容器set  <set>
            2、name确定对象属性名
            3 、确定从表外键的名称   <key>
            4 、确定关系,及关联的另一方的完整类名
            注意:
                在hibernate中可以只进行单向配置。
                每一个配置项都可以完整的描述彼此关系。
                一般情况采用双向配置,双方都可以完成描述表与表之间关系。
        -->
        <set name="orders">
            <key column="customer_id"></key>
            <one-to-many class="Order"/>
        </set>
    </class>
</hibernate-mapping>

Order.hbm.xml

代码语言:javascript
复制
<?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  package="com.itheima.domain">
    <class name="Order" table="t_order">
        <id name="oid" column="oid">
            <generator class="native"></generator>
        </id>
        <property name="oname" column="oname" type="String"></property>

        <!-- 多对一:多个订单属于一个客户
            1、name 确定属性名称
            2、column 确定从表的外键名称
            3、class 关联的另一方的完整类名
        -->
        <many-to-one name="customer" column="customer_id" class="Customer"></many-to-one>
    </class>
</hibernate-mapping>

hibernate.cfg.xml

代码语言:javascript
复制
......
<!-- 添加ORM映射文件 ,填写src之后的路径-->
<mapping resource="com/itheima/domain/Customer.hbm.xml"/>
<mapping resource="com/itheima/domain/Order.hbm.xml"/>
......

测试一对多代码:

代码语言:javascript
复制
package com.itheima.e_one2many;

import java.util.Set;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;

// 测试:一对多实现
public class Demo1 {
    @Test
    // 1 、测试一对多关系中:保存操作
    //      共打印5条语句
    //      前3条打印insert     =>  保存对象,维护外键
    //      后两条打印update    =>  维护外键

    //      维护了两次外键,有些多余,该如何解决呢?
    //      解决  =>  单纯指定关系由其中一方来维护,另一方不维护关系(放弃维护)。
    //      注意  =>  外键维护的放弃,只能由非外键所在对象来放弃。
    //            =>  需要在非外键所在对象的配置文件中进行配置。
    //      本例中:配置Customer的inverse属性为:true
    //      只打印3条语句 =>  外键由Order自己来维护
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = new Customer();
        c.setCname("tom");

        Order o1 = new Order();
        o1.setOname("肥皂");
        Order o2 = new Order();
        o2.setOname("蜡烛");

        // 外键维护的放弃,只能由非外键所在对象来放弃。需要在非外键所在对象的配置文件中进行配置。配置好后,下面两句代码可以省略了。
        // c.getOrders().add(o1); // 维护关系
        // c.getOrders().add(o2); // 维护关系

        o1.setCustomer(c); // 维护关系
        o2.setCustomer(c); // 维护关系

        session.save(c);  // 保存对象
        session.save(o1); // 保存对象
        session.save(o2); // 保存对象

        session.getTransaction().commit();
        session.close(); 
    }
    // 多表关系 =>  删除
    // 直接删除 Customer时 ,会先移除 Customer中引用的外键,然后再删除Customer。
    // 结论: 在维护一方的对象时,会自动维护这一方对象与另一方对象的关系。

    // 当把Customer的 inverse属性设置为:true 后,
    // 再次演示删除会报错    =>  因为Customer不负责维护外键了,直接删除Customer会导致,Order引用了无效的id,违反了外键约束。

    // 那么该如何解决呢?
    // 答:因为现在是通过Order来维护外键的,所以应该先单独设置订单不属于任何Customer后,再删除Customer。

    // 什么时候配置inverse属性呢?
    // 答:主要看业务即(看实际需求)。
    //       如果一的一方经常需要自己来维护外键,那么在一的一方就不要配置inverse属性了。
    //       如果一的一方经常要通过多的一方来维护外键,那么在一的一方要配置inverse属性为true。

    @Test
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = (Customer) session.get(Customer.class, 3);

        // 答:因为现在是通过Order来维护外键的,所以应该先单独设置订单不属于任何Customer后,再删除Customer。
        Set<Order> set = c.getOrders(); // 得到的是持久状态,修改后不需要更新,在提交的时候会自动同步数据库
        for (Order o : set) {
            o.setCustomer(null); // 先单独设置订单不属于任何Customer
        }

        session.delete(c);

        session.getTransaction().commit();
        session.close();
    }
}

4.2、级联操作【读、理解】

4.2.1、save-update 级联保存或更新

示例代码同下:

4.2.2、delete 级联删除

示例代码如下所示:

代码语言:javascript
复制
package com.itheima.e_one2many;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;

// 测试:一对多操作
public class Demo2 {
    @Test
    // 增    =>  级联保存
    // 我们希望在保存Customer时,自动将Customer集合中未保存的瞬时状态对象Order进行保存。并不会维护关系哦!
    // 配置 Customer 的 cascade 属性为: save-update 级联保存和级联修改。

    // 级联保存和反转的对比:
    // 首先在配置级联保存,Customer的cascade属性为: save-update 后
    // 接着配置Customer的inverse属性为:true 后,
    // 会出现一个问题,Order的外键的值为null了。我们怎么使得外键有值呢?
    // 答: 法一:我们可以手动维护关系。如下,此时运行代码,打印3条insert语句。
    //      法二:将Customer的inverse属性改为默认值:false 后,此时运行代码,打印5条sql语句:3条insert,2条update。
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = new Customer();
        c.setCname("tom");

        Order o1 = new Order();
        o1.setOname("肥皂");
        Order o2 = new Order();
        o2.setOname("蜡烛");

        c.getOrders().add(o1); // 维护关系
        c.getOrders().add(o2); // 维护关系

        // 配置Customer的inverse属性为:true 后,配置好后,下面两句代码可以省略了。即可以不用多维护一次外键了。
        // c.getOrders().add(o1); // 维护关系
        // c.getOrders().add(o2); // 维护关系

        // 在配置Customer的inverse属性为:true 后,
        // 但是会出现一个问题,Order的外键的值为null了。我们怎么使得外键有值呢?答:法一:我们可以手动维护关系。
        // o1.setCustomer(c); // 手动维护关系
        // o2.setCustomer(c); // 手动维护关系

        session.save(c); // 保存对象
        // 配置Customer的cascade属性为:save-update 后,配置好后,下面两句代码可以省略了。
        // session.save(o1); // 保存对象
        // session.save(o2); // 保存对象

        session.getTransaction().commit();
        session.close(); 
    }

    @Test
    // 增    =>  级联修改
    // 我们希望在保存Customer时,自动将Customer集合中未保存的瞬时状态对象Order进行保存。并不会维护关系哦!
    // 配置 Customer 的 cascade 属性为: save-update 级联保存和级联修改。
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = (Customer) session.get(Customer.class, 4); // 1条 select 语句

        for (Order o : c.getOrders()) { // 1条 select 语句,根据外键查到的
            o.setOname("哇哈哈"); // 修改订单
        }

        session.getTransaction().commit(); // 2条update 语句,因为该用户有2个订单,又因为设置级联修改,会自动将订单的修改同步(更新)到数据库
        session.close();
    }

    @Test
    // 删除   =>  级联删除
    // 配置 Customer 的 cascade 属性为: delete 级联删除。 
    // 在删除Customer时 ,会将Customer下的订单一并删除。

    // 前提:该用户有2个订单
    // 此时若再配置Customer的  inverse 属性为:
    //      false 时,共6条sql语句
    //      true 时,共5条sql语句, 比上面少一条维护外键语句
    public void fun3() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = (Customer) session.get(Customer.class, 5); // 1条 select 语句

        session.delete(c); // 删除Customer,并删除两个Order,1条 select 语句 

        session.getTransaction().commit(); // 之后,1条 update 语句  + 3条  delete 语句
        session.close();
    }

    @Test
    // 删除   =>  级联删除
    // 配置 Customer 的 cascade 属性为: delete 级联删除。 

    // 操作的两方 cascade 值都为 delete 时
    // 需要注意: 千万不要在两方都配置级联删除,如果这样配置的话,删除任何一方,都会导致整个关系链对象全部删除。
    public void fun4() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Order o = (Order) session.get(Order.class, 9); // 1条 select 语句

        session.delete(o);
        // 找到所有关联的 Customer,1条 select 语句
        // 此时 Customer 也配置了级联删除 =>  1条 select 语句, 找下面的 Order
        // 删除所有 Order
        // 再删除 Customer

        session.getTransaction().commit();
        session.close();
    }
}
4.2.3、孤儿删除

  一对多关系中存在父子关系。1表(主表)可以称为父表,多表(从表)称为子表。 总结:   主表不能删除从表已经引用(关联)的数据。   从表不能添加主表不存在的数据。

代码语言:javascript
复制
package com.itheima.e_one2many;

import java.util.HashSet;
import java.util.Iterator;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;

// 测试:一对多操作:孤儿删除
public class Demo3 {
    @Test
    // 配置 Customer 的 inverse 属性为: false
    // 配置 Customer 的 cascade 属性为: delete-orphan 孤儿删除    =>  当没有任何外键引用Order时,Order会被删除
    // 示例前提:该用户有2个订单
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = (Customer) session.get(Customer.class, 3); // 1条 select 语句

        Iterator<Order> it = c.getOrders().iterator(); // 1条 select 语句,1条 update 语句,2条 delete 语句
        while (it.hasNext()) {  // 遍历Customer下的订单,解决客户和订单的关系
            it.next();          // 默认:客户和订单解除关系后,外键被设置成null,此时订单就是孤儿。客户和订单都存在。
            it.remove();        // 孤儿删除(孤子删除),当订单为孤儿时,一并删除。客户仍存在。
        }

        // 注意: 删除Customer下的订单时,不能使用 下面两句代码:
        // c.setOrders(null); 
        // c.setOrders(new HashSet<Order>());

        session.getTransaction().commit();
        session.close();
    }
}
4.2.4、演示all-delete-orphan

示例代码如下:

代码语言:javascript
复制
package com.itheima.e_one2many;

import java.util.Iterator;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;

//测试:一对多关系:all 和 all-delete-orphan
public class Demo4 {
    @Test
    // 配置 Customer 的 cascade 属性为: all-delete-orphan =>  相当于配置了 save-update,delete,delete-orphan
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = new Customer();
        c.setCname("tom");

        Order o1 = new Order();
        o1.setOname("肥皂");
        Order o2 = new Order();
        o2.setOname("蜡烛");

        c.getOrders().add(o1); // 维护关系
        c.getOrders().add(o2); // 维护关系

        session.save(c);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // 配置 Customer 的 cascade 属性为: all-delete-orphan =>  相当于配置了 save-update,delete,delete-orphan
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = (Customer) session.get(Customer.class, 10);
        session.delete(c);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // 配置 Customer 的 cascade 属性为: all-delete-orphan =>  相当于配置了 save-update,delete,delete-orphan
    public void fun3() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c = (Customer) session.get(Customer.class, 12);

        Iterator<Order> it = c.getOrders().iterator(); // 1条 select 语句,1条 update 语句,2条 delete 语句
        while (it.hasNext()) {  // 遍历Customer下的订单,解决客户和订单的关系
            it.next();          // 默认:客户和订单解除关系后,外键被设置成null,此时订单就是孤儿。客户和订单都存在。
            it.remove();        // 孤儿删除(孤子删除),当订单为孤儿时,一并删除。客户仍存在。
        }

        session.getTransaction().commit();
        session.close();
    }
}
4.2.5、总结

核心配置文件hibernate.cfg.xml内容如下:

代码语言:javascript
复制
<?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  package="com.itheima.domain">
    <class name="Customer" table="t_customer">
        <id name="cid" column="cid">
            <generator class="native"></generator>
        </id>
        <property name="cname" column="cname" type="string"></property>

        <!-- 一对多:一个客户当前客户 拥有 多个订单
            1 、确定容器set  <set>
            2、name确定对象属性名
            3 、确定从表外键的名称   <key>
            4 、确定关系,及关联的另一方的完整类名
            注意:
                在hibernate中可以只进行单向配置。
                每一个配置项都可以完整的描述彼此关系。
                一般情况采用双向配置,双方都可以完成描述表与表之间关系。

            inverse属性:反转    
                是否将关系的维护反转给对方,默认值是:false,
                设置成true表示放弃维护,将维护关系反转。
            cascade属性:级联操作
                save-update:级联保存和级联修改。保存A时,同时保存B
                delete:删除A时,同时删除B,A、B都不存在了。
                delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。
                all:save-update 和   delete 整合
                all-delete-orphan:三个整合  在一起

            如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">
        -->
        <set name="orders" inverse="false" cascade="all-delete-orphan">
            <key column="customer_id"></key>
            <one-to-many class="Order"/>
        </set>
    </class>
</hibernate-mapping>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-07-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Hibernate中对象的状态
  • 二、一级缓存
    • 2.1、Session缓存的细节问题
      • 2.2、一级缓存快照【掌握】
        • 2.3、关于学习Session缓存的几个问题
          • 2.4、Session其他API(大部分都是了解即可)
          • 三、多表设计
          • 四、Hibernate的关联关系映射(一对多)
            • 4.1、一对多实现【掌握】
              • 4.1.1、实现类
              • 4.1.2、配置文件
            • 4.2、级联操作【读、理解】
              • 4.2.1、save-update 级联保存或更新
              • 4.2.2、delete 级联删除
              • 4.2.3、孤儿删除
              • 4.2.4、演示all-delete-orphan
              • 4.2.5、总结
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档