1、Session概述
Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口,它提供了基本的保存、更新、删除和加载Java对象的方法。
每一个Session对象都在内存中维护了一个缓存,位于缓存中的对象称为持久化对象,它和数据库表中的相关记录保持着一种对应关系。通过Session缓存,Hibernate最大限度的减少了应用程序访问数据库的次数,实现了对内存空间更加有效的利用。
Hibernate把持久化类的对象分为4种状态:持久化状态、临时状态、游离状态、删除状态。Session的特定方法能使对象从一个状态转换到另一个状态。
2、Session缓存
在应用系统开发过程中,使用缓存往往是提升程序性能的主要手段。在Hibernate中,Session对象维护的是一个线程级的一级缓存,SessionFactory维护了一个进程级别的二级缓存。二级缓存会在后面的博客中详细介绍,现在先关注Session维护的一级缓存。
2.1 使用Session缓存的好处
①减少访问数据库的次数
两次调用session.get(Student.class,1);方法,仅发送1条SQL语句。原因是第一次使用get()方法加载的Student被缓存到了内存中,第二次调用get()方法直接使用了内存中缓存的Student对象,从而减少了访问数据库的次数。
Tips:应用程序中访问数据库的次数往往是最主要的性能瓶颈。
②将内存中数据的变化自动同步到数据库中
使用session.get(Student.class,1);方法将一个Student对象加载到内存中后,Student对象就处于Session对象的管理中。此时调用student.setStuName(“a”);方法将触发Hibernate对数据库表中对应数据记录的修改,即发送一条UPDATE语句,更新Student对象在数据库表中对应的记录。
Hibernate最大的特点就是将对数据库记录的操作,转换为对Java对象的操作,简化开发。
2.2 Session缓存操作概述
①flush:推送。将缓存中数据的改变落实到数据库中
②refresh:刷新。将数据库中数据的改变提取到缓存中
③clear:清空。清空Session缓存
2.3 flush操作
①Session对象默认在什么情况下执行flush操作? ②默认情况下Session执行flush操作的时机: [1]显式调用Session的flush()方法 [2]调用Transaction对象的commit()方法时,先flush缓存,在提交事务 [3]执行HQL、QBC查询之前,先flush缓存,保证HQL或QBC查询到的数据是最新的。 ③相关问题 [1]修改持久化对象的属性值,并不会立即发送SQL语句,而是提交事务之前,执行flush操作的时候才会发送相应的UPDATE语句。 [2]发送UPDATE语句时,数据的修改并不会立即生效,只有提交了事务之后,数据的修改才会永久的保存下来。2.4 refresh操作与事务隔离级别
①提出问题 在使用MySQL客户端修改数据后,refresh()方法读取到的还是旧的未修改的数据,为什么? ②分析 MySQL默认的数据库事务隔离级别是“可重复读”,要保证当前事务执行期间的不同时间点读取到的数据是一样的。 ③结论 refresh()方法读取到的数据是不是真正的最新数据,需要参照当前的事务隔离级别。 3 事务隔离级别回顾 3.1 数据库事务并发问题 假设现在有两个事务:Transaction01和Transaction02并发执行。 ①脏读 [1]Transaction01将某条记录的AGE值从20修改为30。 [2]Transaction02读取了Transaction01更新后的值:30。 [3]Transaction01回滚,AGE值恢复到了20。 [4]Transaction02读取到的30就是一个无效的值。 ②不可重复读 [1]Transaction01读取了AGE值为20。 [2]Transaction02将AGE值修改为30。 [3]Transaction01再次读取AGE值为30,和第一次读取不一致。 ③幻读 [1]Transaction01读取了STUDENT表中的一部分数据。 [2]Transaction02向STUDENT表中插入了新的行。 [3]Transaction01读取了STUDENT表时,多出了一些行。 3.2 隔离级别 数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。 ①读未提交:READ UNCOMMITTED 允许Transaction01读取Transaction02未提交的修改。 ②读已提交:READ COMMITTED 要求Transaction01只能读取Transaction02已提交的修改。 ③可重复读:REPEATABLE READ 确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。 ④串行化:SERIALIZABLE 确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。 ⑤各个隔离级别解决并发问题的能力见下表⑥各种数据库产品对事务隔离级别的支持程度
⑦在MySQL中设置隔离级别
[1]每启动一个MySQL程序,就会获得一个单独的数据库连接。每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。
[2]查看当前的隔离级别:
SELECT @@tx_isolation;
[3]设置当前MySQL连接的隔离级别:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
[4]设置数据库系统的全局的隔离级别:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
⑧在Hibernate中设置隔离级别
[1]JDBC数据库连接使用数据库系统默认的隔离级别。
[2]在Hibernate的配置文件中可以显式的设置隔离级别。每一个隔离级别都对应一个整数:
[3]设置方式
2
4 持久化对象的状态
4.1 四种状态 ①临时状态 [1]OID:没有OID。例如:new Student(null, "Kate2015", 18, new Date()); [2]是否在Session缓存中:不在 [3]是否在数据库中有对应的记录:没有 ②持久化状态 [1]OID:有OID。例如:Student student = (Student) session.get(Student.class, 1); [2]是否在Session缓存中:在 [3]是否在数据库中有对应的记录:有 ③游离状态 [1]OID:有OID。例如:new Student(1, "Kate2015", 18, new Date()); [2]是否在Session缓存中:不在 [3]是否在数据库中有对应的记录:有 ④删除状态 [1]OID:有OID。通常是经过删除操作得到的。 [2]是否在Session缓存中:不在 [3]是否在数据库中有对应的记录:没有4.2 四种状态之间的转换
5 Session核心方法
5.1 save() ①将一个临时对象转换为持久化对象 ②对于OID:忽略临时对象中的“OID”值,save()方法会将数据库产生的新的OID的值赋值给持久化对象 ③持久化状态的对象的OID的值是不允许修改的 5.2 persist() ①作用和save()相同 ②不允许保存带有OID的对象,会抛异常:org.hibernate.PersistentObjectException 5.3 get() ①根据OID的值,从数据库中立即加载一个对象到内存中。 ②如果调用get()方法时,Session缓存中已经有了一个OID相同的对象,则get()方法会将缓存中的对象返回,不发送SQL语句。 5.4 load() ①采用延迟检索策略,在调用load()方法时仅返回一个代理对象,不发送SQL语句。在真正用到非OID属性值时,才发送SQL语句进行查询。 ②懒加载初始化异常 [1]全类名:org.hibernate.LazyInitializationException [2]产生原因:对一个延迟加载的代理对象进行初始化时,Session缓存中没有对应OID的对象,无法完成初始化。 5.5 update() ①根据游离对象或持久化对象对数据库表进行更新 ②update()方法更新一个游离对象时,默认情况下无法获知当前游离对象中的数据和数据库表中的数据是否有差异。所以,无论是否有差异都会发送SQL语句。为了避免盲目的发update语句,可以在hbm配置文件的class元素中设置select-before-update属性的值为true。通常不设置这个属性。 ③使用update()方法更新一个OID不存在的对象会抛出异常:org.hibernate.StaleObjectStateException ④使用update()方法更新一个游离对象,但此时Session缓存中已经存在了OID相同的持久化对象,会抛出异常:org.hibernate.NonUniqueObjectException 5.6 saveOrUpdate() ①兼具保存和更新两种功能。传入临时对象执行保存,传入游离对象执行更新。 ②判断执行INSERT语句还是UPDATE语句的依据 [1]根据OID的值,如果为null则执行保存,否则执行更新 [2]如果OID的值等于hbm文件中,id元素设置的unsaved-value属性则执行保存操作。 5.7 delete() ①执行删除操作 ②删除对象后将OID的值置空:在Hibernate配置文件中加入如下配置true
5.8 doWork()
①用于在Hibernate环境下执行原生的JDBC代码 ②示例代码@Testpublic void testWork() { session.doWork(new Work() { @Override public void execute(Connection connection) throws SQLException { //使用传入的Connection对象实现原生的JDBC操作... } });}
GitHub地址:https://github.com/leebingbin/