Hibernate对象的状态

时间:2023-03-09 01:58:54
Hibernate对象的状态

    站在持久化的角度, Hibernate 把对象分为 4 种状态:

      1. 持久化状态

      2. 临时状态

      3. 游离状态

      4. 删除状态

    Session 的特定方法能使对象从一个状态转换到另一个状态.

    下面是各状态之间的转换图, 以及如何转换的:

Hibernate对象的状态

准备工作:

  1. 建立持久化类

    Person类

package com.hibernate.entities;

public class Person {

    private Integer id;
    private String name;
    private Integer age;

    public Person() {
        super();
    }

    public Person(String name, Integer age) {
        super();
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

  2. 建立持久化类的映射文件

    Person.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.hibernate.entities.Person" table="PERSONS">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="age" type="java.lang.Integer">
            <column name="AGE" />
        </property>
    </class>
</hibernate-mapping>

  3. 建立Hibernate的主配置文件 hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>

        <!-- 配置连接数据库的信息 -->
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">oracle</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

        <!-- 配置数据库方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL57InnoDBDialect</property>

        <!-- 设置数据表生成策略 -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- 是否格式化SQL -->
        <property name="hibernate.format_sql">true</property>

        <!-- 是否显示SQL -->
        <property name="hibernate.show_sql">true</property>

        <!-- 修改delete()的默认行为, 使删除对象的OID变为null -->
        <property name="hibernate.use_identifier_rollback">true</property>

        <!-- 添加配置文件 -->
        <mapping resource="com/hibernate/entities/Person.hbm.xml"/>

    </session-factory>
</hibernate-configuration>

  4. 建立单元测试类

package com.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class TestHibernate {

    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;

    @Before
    public void init(){

        sessionFactory = new Configuration().configure().buildSessionFactory();
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    }

    @After
    public void distory(){
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}

  这里我直接把注释, 说明等文字写在代码里了,可以直接复制下来, 放到IDE开发工具里面慢慢看

package com.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.hibernate.entities.Person;

public class TestHibernate {

    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;

    @Before
    public void init(){

        sessionFactory = new Configuration().configure().buildSessionFactory();
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    }

    @After
    public void distory(){
        transaction.commit();
        session.close();
        sessionFactory.close();
    }

    /*
     * 站在持久化的角度:
     * Hibernate 把对象分为 4 种状态:
     *         持久化状态, 临时状态, 游离状态, 删除状态.
     * Session 的特定方法能使对象从一个状态转换到另一个状态.
     */
    /*
     * 一: 临时对象(Transient)
     *     1. 在使用代理主键的情况下, OID 通常为 null
     *     2. 不处于 Session 的缓存中
     *     3. 在数据库中没有对应的记录
     */
    /*
     * 二: 持久化对象(也叫"托管")(Persist)
     *     1. OID 不为 null.
     *  2. 位于Session缓存中.
     *  3. 若在数据库中已经有和其对应的记录, 持久化对象和数据库中的相关记录对应.
     *  4. Session在flush缓存时, 会根据持久化对象的属性变化, 来同步更新数据库.
     *  5. 在同一个Session实例缓存中, 数据库表中的每条记录只对应唯一的持久化对象.
     */
    /*
     * 三: 删除对象(Removed)
     *  1. 在数据库中没有和其OID对应的记录.
     *  2. 不再处于Session缓存中.
     *  3. 一般情况下, 应用程序不再使用该对象.
     */
    /*
     * 四: 游离对象(也叫"托管")(Detached)
     *  1. OID不为null.
     *  2. 不再处于Session缓存中.
     *  3. 一般情况下, 游离对象是持久化对象转变过来的, 因此在数据库中可能还存在与它对应的记录.
     */

    /**
     * save()方法:使一个临时对象转变为持久化对象.
     * 执行save()方法时, 系统会完成以下操作:
     *  1. 把 new 对象加入到Session缓存中, 使它进入持久化状态.
     *  2. 选用映射文件指定的标示符生成器, 为持久化对象分配唯一的OID.
     *      在使用中代理主键的情况下, setId() 方法为 new 对象设置的OID 是无效的.
     *  3. 计划执行一条 insert 语句(在flush缓存的时候).
     * persist()方法: 同save()方法.
     */
    @Test
    public void testSaveOrPersist(){

        // 保存一个对象
//        Person person = new Person("Tom", 12);
//        session.save(person);

        // 使用 save() 保存一个有id值的对象时, 正常保存.
//        Person person = new Person("Tom", 12);
//        person.setId(100);
//        session.save(person);

        // 使用 persist()保存一个有id值得对象时, 抛异常!
//        Person person = new Person("Mike", 23);
//        person.setId(100);
//        session.persist(person);

        // 试图修改持久化对象的 ID, 不允许!
//        Person person = session.get(Person.class, 1);
//        person.setId(1000);
//        session.update(person);

    }

    /**
     * get() 和 load() 方法
     * 相同点:
     *     都可以根据指定的OID, 从数据库中加载一个持久化对象.
     * 不通电:
     *     1. 当数据库不存在OID对应的记录时:
     *         load() 会抛出异常.
     *         get() 返回null.
     *  2. 两者采用不同的加载策略:
     *      load() 支持延迟加载.
     *      get() 不支持.
     */
    @Test
    public void testGetOrLoad(){

        // 1. 使用 get() 获取对象
        // 当调用 get() 时, Hibernate会计划执行SQL, 在flush时的时候, 会发送SQL语句.
//        session.get(Person.class, 2);
//        session.flush();
//        System.out.println("======================");

        // 2. 使用  load() 延迟加载对象
        // 当调用 load() 时, Hibernate不会计划执行SQL
        // 即使flush操作的时候, 如果没有使用这个对象, 那么Hibernate就不会发送SQL语句.
        // 只有在使用这个对象的时候, Hiberante才会发送SQL语句. 这就是延迟加载!
        Person person = session.load(Person.class, 2);
        session.flush();
        System.out.println("======================");
        System.out.println(person.getName());

    }

    /**
     * 1. update(): 使一个游离对象转变为持久化对象, 并且计划执行一条SQL语句.
     * 2. 如果希望Session仅当修改了 new 对象的属性时, 才执行 update 语句
     * 可以把映射文件中的<class>元素的 select-before-update 设置为true, 默认为false.
     * 3. 当 update() 关联一个游离对象时, 如果在Session的缓存中已经存在相同OID的持久化对象, 会抛异常!
     * 4. 当 update() 关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常!
     */
    @Test
    public void testUpdate(){

        // 一: 正常的 update
//        // tip:游离对象是由持久化对象转换过来的.
//        Person person = session.get(Person.class, 1);
//        transaction.commit();
//        // session 关闭后, person 对象转换为游离对象.
//        session.close();
//        session = sessionFactory.openSession();
//        transaction = session.beginTransaction();
//        person.setAge(19);
//        // 执行完 update, person对象由游离对象转变为持久化对象.
//        session.update(person);

        // 二: 当在配置文件添加 select-before-update=true 的时候
        // 这里我们用注释来做, 在类上加上: @SelectBeforeUpdate(value=true)
        // 1. 没有改变任何属性, 当时Hibernate仍然发送一条SQL语句来验证属性值是否改变.
//        Person person = session.get(Person.class, 1);
//        session.update(person);
        // 2. 改变了属性之后, 不仅发送一条SQL语句, 还会发送一条update语句.
//        Person person = session.get(Person.class, 1);
//        person.setName("Kim");
//        session.update(person);

        // 三: Session缓存中存在相同的OID.
//        // 先获取一个持久化Person, 然后关闭Session变为游离态.
//        Person person1 = session.get(Person.class, 1);
//        transaction.commit();
//        session.close();
//        session = sessionFactory.openSession();
//        transaction = session.beginTransaction();
//        // 开启Session, 再获取一个相同的Person对象
//        Person person2 = session.get(Person.class, 1);
//        person1.setName("Sunny");
//        // 把游离态的person1转化为持久化态, 由于我们缓存中已经有一个OID为1的person2
//        // 所以抛异常!(Hibernate不允许缓存中存在OID相同的对象)
//        session.update(person1);

        // 四: 数据库中不存在相应的记录
        // 意味着这是一个临时对象, 或者删除对象
        // 只能执行save()或者persist()进行持久化
        // update() 只能把游离对象, 转化为持久化对象
//        Person person = new Person("Lily", 15);
//        // 抛异常!
//        session.update(person);

    }

    /**
     * saveOrUpdate(): 包含了 save() 和 update() 的功能
     * 如果是一个临时对象, 就执行save()
     * 如果是一个持久化对象, 就执行update()
     * 判断是临时对象的标准:
     *     1. 如果对象的OID为null.
     *  2. 映射文件中<id> 设置了 unsaved-value 属性,
     *      并且java对象的OID取值与这个unsaved-value属性值匹配.
     */
    @Test
    public void testSaveOrUpdate(){

        // id(OID)值为null, 执行 insert 语句
//        Person person = new Person("Mike", 15);
//        session.saveOrUpdate(person);

        // id(OID)值为unsaved-value的值(100), 执行insert语句
//        Person person = new Person("Mike", 15);
//        person.setId(100);
//        session.saveOrUpdate(person);

        // 数据库中有与id(OID)值对应的值, 那么就执行update语句
        Person person = new Person("Mike", 15);
        // 数据库中有主键为2的数据, 执行update语句
        person.setId(2);
        session.saveOrUpdate(person);

    }

    /**
     * merge()
     */
    @Test
    public void testMerge(){

        /*
         * 一: 临时对象
         * 创建一个新的News对象,把new1对象的属性拷贝到新建的News对象中
         * 持久化这个News对象,计划执行一条insert语句
         */
//        Person person = new Person("Tom", 13);
//        // 发送了一条insert
//        session.merge(person);

        /*
         * 二: 游离对象 + 存在Session缓存中
         */
//        Person person1 = session.get(Person.class, 1);
//        // guanbiSession, 开启Session使对象变为游离态
//        transaction.commit();
//        session.close();
//        session = sessionFactory.openSession();
//        transaction = session.beginTransaction();
//        // 重新加载到Session缓存中.
//        Person person2 = session.get(Person.class, 1);
//        person1.setName("Mike");
//        // 把person1对象的属性拷贝到person2持久化对象中,计划执行一条update语句
//        // 发送了2条select, 1条update
//        session.merge(person1);

        /*
         * 三: 游离对象 + 不存在Session缓存中 + 数据库中存在id为1的记录
         */
//        Person person = session.get(Person.class, 1);
//        transaction.commit();
//        session.close();
//        session = sessionFactory.openSession();
//        transaction = session.beginTransaction();
//        person.setName("Kim");
//        // 从数据库加载id为1的News持久化对象(会发送一条select)
//        // 把person对象的属性拷贝到News持久化对象中,计划执行一条update语句
//        // 发送了2条select, 1条update
//        session.merge(person);

        /*
         * 四: 游离对象 + 不存在Session缓存中 + 数据库中不存在id为1的记录
         */
//        // 1 条select
//        Person person = session.get(Person.class, 1);
//        // 从数据库中删除, 1 条delete
//        session.delete(person);
//        transaction.commit();
//        session.close();
//        session = sessionFactory.openSession();
//        transaction = session.beginTransaction();
//        // 1 一条insert
//        session.merge(person);

    }

    /**
     * delete(): 既可以删除一个游离对象, 也可以删除一个持久化对象
     * 处理过程:
     *         1. 计划执行一条 delete 语句
     *         2. 把对象从 Session缓存中移除, 该对象进入删除状态
     * hibernate.use_identifier_rollback 属性:
     *         这个值默认为false, 如果改为true, 将改变 delete()的运行行为,
     *         delete()会把持久化对象或游离对象的OID置为null, 使它们变为临时对象
     */
    @Test
    public void testDelete(){

        // 1. hibernate.use_identifier_rollback=false
//        Person person = session.get(Person.class, 3);
//        session.delete(person);
//        session.flush();
//        // 打印 3
//        System.out.println(person.getId());

        // 2. hibernate.use_identifier_rollback=true
        Person person = session.get(Person.class, 3);
        session.delete(person);
        session.flush();
        // 打印 null
        System.out.println(person.getId());

    }

}