前言:Session接口是Hibernate向应用程序提供的操作数据库的主要接口,它提供了基本的增删查改方法,而且Session具有一个缓存它是Hibernate的一级缓存。站在持久化层的角度,Hibernate把实体类分为4种状态:持久化状态、临时状态、游离状态和删除状态。本文将会介绍Hibernate这些机制的细节以及使用方法。

1.Hibernate的Session缓存

(1)Session缓存的作用
    使用Session的缓存有三大作用:
    1. 减少访问数据库的频率,当Session的get()方法试图从数据库中加载一个对象时,Session先判断缓存中是否存在这个对象,如果存在就不需要再从数据库中检索,而直接从缓存中获取这个对象。
    2. 当缓存中的持久化对象之间存在循环关系是,Session会保证不会出现访问对象图的死循环,以及由循环引起的JVM堆栈溢出异常。
    3. Session能保证数据库中的相关记录与缓存中相应的对象保持同步。Session在清理缓存的时候会自动进行脏检查,如果发现Session缓存中的对象与数据库中的相应的记录不一致,就会根据对象的最新属性去同步更新数据库。
(2)Hibernate的脏检查和清理Session缓存原理
    当Session缓存中的对象的属性发生变化时,Session并不会立即清理缓存及执行相关的SQL update语句,而是在特定的时间点才清理缓存,这使得Session能把几条相关的SQL语句合并成一条SQL语句,以便减少访问数据库的次数。
    Session在清理缓存时,会按照以下顺序执行SQL:先insert,再update,最后delete。具体如下:
    1. 按照应用程序调用session.save()方法的先后顺序,执行所有对实体类进行插入的insert语句。
    2. 执行所有对实体类进行更新的update语句。
    3. 执行所有对实体类集合进行删除的delete语句。
    4. 执行所有对实体类集合进行删除、更新或者插入的SQL语句。
    5. 执行所有对实体类集合进行插入的insert语句。
    6. 按照应用程序调用session.delete()方法的先后顺序,执行所有对实体类进行删除的delete语句。
    在默认情况下,Session以下情况下清理缓存:
    1. 当应用程序调用org.hibernate.Transaction的commit()方法时,commit()方法先清理缓存,再提交事务。
    2. 当应用程序调用Hibernate执行查询时,如果缓存中的对象与数据库中的不一致(即缓存中的对象已发生变化),Hibernate就会先清理缓存再执行查询,以保证查询得到正确的数据。
    3. 当应用程序显式调用Session的flush()方法时。
    注意:Session清理缓存的例外情况是,如果对象使用native生成OID,那么当调用Session的save()方法时,会立即执行insert语句,以确保立即从数据库中得到对象的OID。
    如果不希望Session在以上默认的时间点清理缓存,也可以通过调用session.setFlushMode(flushMode)方法设置清理缓存的模式。具体模式如下:

清理缓存的模式

查询

commit()方法

flush()方法

FlushMode.AUTO

清理

清理

清理

FlushMode.COMMIT

不清理

清理

清理

FlushMode.NEVER

不清理

不清理

清理

    注意:Session的commit()方法与flush()方法的区别,它们都会清理缓存,但是commit()会提交事务。

2.实体类在Hibernate容器中的状态

(1)Hibernate实体类的三种状态
    站在持久化的角度,一个Java实体类的生命周期可以有四种状态,如下:
    1. 临时状态(transient):刚用new语句创建,还未被持久化的并且不在Session的缓存中的实体类。
    2. 持久化状态(persistent):已被持久化,并且在Session缓存中的实体类。
    3. 删除状态(removed):不在Session缓存中,而且Session已计划将其从数据库中删除的实体类。
    4. 游离状态(detached):已被持久化,但不再处于Session的缓存中的实体类。
    实体类的状态是可以变化的,它的变化过程如下图:
          
(2)实体类生命周期状态的特征
    临时状态实体类的特点:
    1. 在使用代理主键的情况下,OID为null。
    2. 不在Session缓存中。
    3. 在数据库中没有对应的记录。
    持久化状态实体类的特点:
    1. OID不为null。
    2. 在Session缓存中
    3. 数据库中有对应的记录。
    删除状态实体类的特点:
    1. OID不为null。
    2. 不在Session缓存中。
    3. Session计划将其从数据库中删除。
    4. Session在清理缓存时,会执行相应的delete语句。
    游离状态实体类的特点:
    1. OID不为null。
    2. 不在Session缓存中。
    3. 数据库中有对应的记录。

3.Session接口的详细用法

    Session接口是Hibernate向应用程序提供操作数据库的最主要的接口,本小节就讲解它的主要使用的方法。
(1)创建SessionFactory的过程
    一般情况下我们是通过SessionFactory得到Session对象的,SessionFactory是一个重量级的对象,一般应用程序中一个数据库对应一个SessionFactory对象,而Session对象是轻量级每次操作数据库时就会创建一个Session对象,它和JDBC的Connection接口类似。创建SessionFactory对象的一般步骤如下:
  1. 创建Configuration对象,调用Configuration对象读取配置文件。
  2. 创建ServiceRegistry对象,注册Hibernate服务。
  3. 调用Configuration.buildSessionFactory(ServiceRegistry)方法创建SessionFactory对象。
    创建对象的完整例子:
  1. Configuration cfg = new Configuration();
  2. cfg.configure();
  3. ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
  4. SessionFactory sf = cfg.buildSessionFactory(sr);
    读取Hibernate配置文件(hibernate.cfg.xml),有如下三种方式:
  1. // 1.读取默认名称的配置文件
  2. cfg.configure();
  3. // 2.读取指定名称的配置文件
  4. cfg.configure("hibernate.cfg.xml");
  5. // 3.程序设置配置属性
  6. cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
  7. .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
  8. .setProperty("hibernate.order_updates", "true");
    读取对象—关系映射文件,有如下两种方式:
  1. // 1.读取配置文件
  2. cfg.addResource("Student.hbm.xml").addResource("Teacher.hbm.xml");
  3. // 2.读取类
  4. cfg.addClass(vo.Student.class).addClass(model.Teacher.class);
    得到SessionFactory,得到了该对象就可以得到很多Session对象,有如下两种方式:
  1. // 1.推荐的方式
  2. ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
  3. SessionFactory sf = cfg.buildSessionFactory(sr);
  4. // 2.此种方式已过时
  5. SessionFactory sessions = cfg.buildSessionFactory();
    得到Session对象的两种方式以及区别:
  1. // 1.要配置"hibernate.current_session_context_class",如果前一个Session未close,从上下文得到Session,不一定会new一个新的。建议使用。
  2. Session s=sf.getCurrentSession();
  3. // 2.一定会new一个新的Session
  4. Session s=sf.openSession();
    注意:getCurrentSession()方法创建的Session对象提交事务时就会自动关闭Session,不必调用close()方法。而openSession()不会,需要调用close()方法关闭Session。
(2)Session的save()和persist()方法
    Session的save()与persist()方法都是使一个临时对象转变为持久化对象。save()与persist()方法的区别在于,persist()方法是在Hibernate3版本中才出现的,它实现了EJB3规范中定义的持久化语义。persist()方法不保证立即为持久化对象的OID赋值,而是有可能在Session清理缓存时才为OID赋值。此外如果在事务边界以外调用persist()方法,那么该方法不会计划执行insert语句,这可以提高负责处理长时间运行事务的程序的健壮性,而对于save()方法,不管是在事务边界以外还是事务边界以内调用它,它都会计划执行insert语句。
    对于使用save()方法需要注意以下几点:
    1. 执行save()方法并不会立即执行insert语句,如果调用save()方法后又修改了实体类的属性,那么在Session清理缓存时会执行额外的update语句。
    2. 在使用代理主键的场合,无论Java对象处于临时状态、持久化状态、删除状态还是游离状态,应用程序都不应该修改它的OID,因此比较安全的做法是把实体类的setId()方法设置成protected类型。
    3. save()方法是用来保存临时对象的,在应用程序中不应该把持久化对象或游离对象传给save()方法。
(3)Session的get()和load()方法
    Session的load()方法和get()方法都能根据给定的OID从数据库中加载一个持久化对象,他们的区别在于:当数据库不存在OID对应的记录时,load()方法会抛出org.hibernate.ObjectNotFoundException异常,而get()方法会返回null。此外,load()方法会采用延迟检索策略,而get()方法会立即从数据库检索数据。
(4)Session的update()和saveOrUpdate()方法
    update()方法和saveOrUpdate()方法都有更新数据的功能,他们的详细使用如下:
    1. Session的update()方法可以使一个游离对象转变为持久化对象,并且执行一条update语句,对于同一个实体类即是多次修改了其属性,在清理缓存时也只会执行一条update语句。
    2. 默认情况下,只要调用了update()方法在清理缓存时就会执行一条update语句,如果希望只有修改了实体类属性值才执行update语句,可以设置<class>元素的属性select-before-update="true",这样在update前会先执行一条select语句,对比要更新的实体类属性是否被修改了,只有在修改了实体类属性的情况下才会执行update语句。
    3. saveOrUpdate()方法同时包含了update()与save()的功能,如果传入的是临时对象就调用save(),如果传入的是游离对象就调用update(),如果传入的是持久化对象就直接返回。
    在执行saveOrUpdate()方法时Hibernate判断实体类是不是临时对象的规则,如果满足一下情况之一,Hibernate就把它当作是临时对象:
    1. OID为null。
    2. 实体类有version版本控制属性并取值为null。
    3. 在映射文件为<id>元素设置了unsaved-value属性,并且OID值与其匹配。
    4. 在映射文件中为version版本控制属性设置了unsaved-value属性,并且实体类的version值与其匹配。
    5. 为Hibernate的Interceptor(拦截器)提供了自定义的实现,并且Interceptor实现的isUnsaved()方法返回true。
(5)Session的merge()方法
    Session的merge()方法能够把一个游离对象的属性复制到一个持久化对象中。主要用在如下场景:当Session调用update方法关联一个游离对象时,如果在Session缓存中已经存在同一个类型并且OID相同的持久化对象时,update方法就会抛出NonUniqueException异常,此时就可以调用此方法。
(6)Session的delete()方法
    delete()方法用于从数据库中删除一条记录,它既可以删除游离对象也可以删除持久化对象,delete()方法的处理过程如下:
    1. 如果传入的参数是游离对象,先使游离对象被当前Session关联,使他它变为持久化对象。如果传入的是持久化对象,则忽略这一步。之所以让游离对象变成持久化对象,是为了在使用Interceptor(拦截器)时,Interceptor能正常工作。
    2. 计划执行一个delete语句。
    3. 把对象从Session缓存中删除,该对象进入删除状态。
(7)Session的replicate()方法
    Session的replicate()方法能把一个数据库中的对象赋值到另一个数据库中。例如:
  1. Session session1 = sessionFactory1.openSession();
  2. Teacher teacher =(Teacher) session1.get(Teacher.class, new Long(1));
  3. session1.close();
  4. Session session2 = sessionFactory2.openSession();
  5. Transaction transaction = session2.beginTransaction();
  6. session2.replicate(teacher, ReplicationMode.LATEST_VERSION);
  7. transaction.commit();
  8. session2.close();
    以上的sessionFactory1与sessionFactory2分别对应不同的数据库。

4.Hibernate级联操作实体类

    在实际应用中,对象与对象之间是相互关联的。因此,在Session缓存中存放的是一幅相互关联的对象图。例如代码:session1.get(Teacher.class, new Long(1)),看似仅加载了一个Teacher对象,实际上,如果在关联级别上设置了立即检索,那么Session会自动加载所有跟Teacher对象关联的对象。对于cascade属性的可选值如下:
  1. "none":当Session操纵当前对象时,忽略其他关联的对象,这是默认值。
  2. "save-update":当调用save()、update()、saveOrUpdate()时,级联保存临时对象,级联更新游离对象。
  3. "persist":当调用persist()保存对象时,级联保存临时对象。
  4. "merge":当调用merge()方法时,会级联融合所有关联的游离对象。
  5. "delete":当调用delete()方法时,会级联删除关联的对象。
  6. "lock":当调用Session的lock()方法时,会把关联的游离对象加到Session缓存中。
  7. "replicate":当调用replicate()方法时,会级联复制所有关联的对象。
  8. "evict":当调用evict()方法时,会清除所有关联的对象。
  9. "refresh":当调用refresh()方法时,会刷新所有关联的对象。
  10. "all":包含save-update、persist、merge、delete、lock、replicate、evict、refresh的级联操作行为。
  11. "delete-orphan":删除所有和当前对象解除关联关系的对象。
  12. "all-delete-orphan":包含all和delete-orphan的行为。
-------------------------------------------------------------------------------------------------------------------------------

06.Hibernate实体类生命周期的更多相关文章

  1. Hibernate学习(4)- Hibernate对象的生命周期

    1.Hibernate对象的生命周期(瞬时状态.持久化状态.游离状态) 1.瞬时状态(Transient): 使用new操作符初始化的对象就是瞬时状态,没有跟任何数据库数据相关联:2.持久化状态(Pa ...

  2. spring+hibernate实体类注解详解(非原创) + cascade属性取值

    @Entity //继承策略.另一个类继承本类,那么本类里的属性应用到另一个类中 @Inheritance(strategy = InheritanceType.JOINED ) @Table(nam ...

  3. eclipse从数据库逆向生成Hibernate实体类

    做项目必然要先进行数据库表设计,然后根据数据库设计建立实体类(VO),这是理所当然的,但是到公司里做项目后,让我认识到,没有说既进行完数据库设计后还要再“自己”建立一变VO.意思是,在项目设计时,要么 ...

  4. [转]eclipse借助hibernate tool从数据库逆向生成Hibernate实体类

    如何从数据库逆向生成Hibernate实体类呢??? 1. 首先,要在eclipse中采用自带的数据库管理器(Data Management),连通你的数据库: 然后选择数据库,这里用的oracle, ...

  5. Intellij idea生成Hibernate实体类

    反向生成基于注解的Hibernate实体类 1. 为项目添加Hibernate支持 2. 在IDE右边找到database,然后按照步骤添加数据. 3. 保存后.在主面板左侧有persistence, ...

  6. (转)JVM类生命周期概述:加载时机与加载过程

    原文地址: http://blog.csdn.net/justloveyou_/article/details/72466105 JVM类加载机制主要包括两个问题:类加载的时机与步骤 和 类加载的方式 ...

  7. Eclipse从数据库逆向生成Hibernate实体类和映射文件(Eclipse插件系列之HibernateTools)

    ♣下载安装Eclipse插件(HibernateTools) ♣Eclipse连接数据库(Mysql5.7) ♣新建hibernate.properties和hibernate.cfg.xml文件 ♣ ...

  8. (转) Eclipse通过HibernateTools实现逆向生成Hibernate实体类

    背景:工作中使用Hibernate进行持久化的开发工作,所以有必要详细了解这方面的知识. ps:这里有个问题就是刷新表的时候速度太慢了.还不如自己手动去创建.如果表太多倒是可以采取批量生成的策略. 在 ...

  9. JVM类生命周期概述:加载时机与加载过程

    一个.java文件在编译后会形成相应的一个或多个Class文件,这些Class文件中描述了类的各种信息,并且它们最终都需要被加载到虚拟机中才能被运行和使用.事实上,虚拟机把描述类的数据从Class文件 ...

随机推荐

  1. wget下载FTP的文件

    在Linux中我们怎么样实现wget来下载文件  下面例子 下载所有的py结尾的文件到当前目录 wget ftp://anymous:anymous@42.51.152.2/soft/*.py 递归的 ...

  2. Ajax+Asp.Net无刷新分页

    1.新建解决方案,并建立四个项目BLL,DAL,Model,PagerTest,如图所示: 2.Model代码 using System; using System.Collections.Gener ...

  3. mariadb的explain分析及InnoDB存储引擎

    id: 当前查询语句中,每个SELECT语句的编号,     id: 1  表示简单类型的查询 复杂类型的查询有三种:简单子查询,用于FROM中的子查询,联合查询:UNION 注意:UNION查询的分 ...

  4. MongoDb 与 Nodejs服务器的启动

    1) 启动MongoDB : MongoDB —dbpath databaseNameFolder. 2) 启动数据库 : Mongo DatabaseName. 3) 启动NodeJs: Node ...

  5. 安装Spark集群(在CentOS上)

    环境:CentOS 6.4, Hadoop 1.1.2, JDK 1.7, Spark 0.7.2, Scala 2.9.3 1. 安装 JDK 1.7 yum search openjdk-deve ...

  6. c++ 类与函数中static变量初始化问题(转)

    首先static变量只有一次初始化,不管在类中还是在函数中..有这样一个函数: void Foo() { ; // initialize std::cout << a; a++; } 里的 ...

  7. Oracle sql trace

    一.SQL_TRACE说明 1.1.在全局启用 在参数文件(pfile/spfile)中指定:sql_trace =true 1.2.在当前session级设置 启用当前session的跟踪: alt ...

  8. Java jdk环境变量配置

    首先安装jdk,现在已经是jdk 8了,也不知道能不能好好用了...

  9. Linux中JDK环境变量的配置

    在JDK安装好以后,需要进行环境变量的配置 配置目录   /etc/profile 在这个文件的末尾追加 JAVA_HOME=/home/j2sdk1.4.2_11PATH=$PATH:/home/j ...

  10. golang的"..."备忘

    1. 用于数组: 表示长度与元素个数相同. 在golang中数组的长度是类型的一部分,不同长度,不同类型. 2. 用于参数: 用于形参表示可变参数. 用于实参表示直接传递. 具体解释参数见官方文档: ...