Hibernate Session
- session 实例的生命周期
- session 缓存(一级缓存)
Session 缓存:
- flush() 方法:同步缓存修改的内容至数据库,commit()事务提交时会先进行flush操作,调用此函数可能会发送一系列sql至数据库(缓存数据的状态与数据库数据的状态是否一致),但不提交事务
- refresh() 方法:从数据库重新查询最新的数据,覆盖缓存内容的修改,调用此函数一定会发送sql至数据库,但是需要注意如果数据库隔离级别为可重复读时,会导致同一事务内相同sql所读的数据是一致的
- clear() 方法:清空缓存
- evict() 方法:指定某对象缓存过期,即调用后无法进行隐式更新数据库
- 同一个session不允许持有多个相同OID的对象,否则抛出异常
- 有些操作会导致提前进行flush操作:
1. HQL和Criteria的查询,每次进行查询前,都先会将session缓存中已经被修改的游离状态对象flush到数据库,然后再进行查询,为了保证每次查询都能得到最新的结果
2. OID需要使用数据库生成主键时(native)调用save()方法,因为调用完save方法后必须保证OID非null,所以会导致先发送sql至数据库,如果使用其他主键生成方式,不会提前sql发送
同一个session不允许持有多个相同OID的对象,否则抛出异常:
==============================================
// session - 1
Session session = ourSessionFactory.openSession();
Transaction transaction = session.beginTransaction();
// session - 1 obj
StudentEntity entity1 = session.get(StudentEntity.class, 1);
transaction.commit();
session.close();
// session - 2
session = ourSessionFactory.openSession();
transaction = session.beginTransaction();
// session - 2 OID==1的对象在缓存中已存在
StudentEntity entity2 = session.get(StudentEntity.class, 1);
entity1.setName("haha");
// 这时候缓存中就出现了两个OID==1的对象,抛异常
session.update(entity1);
transaction.commit();
session.close();
ourSessionFactory.close();
Session对象的四种状态以及转换
- 临时状态:通过new产生的对象,调用save、saveOrUpdate、persist等方法变成持久化状态
- 持久化状态:已经被持久化(OID非空且与数据库主键一一对应),加入到Session的缓存中,可调用delete方法变为删除状态,调用clear、evict等方法变为游离状态
- 游离状态:已经被持久化,但不再处于Session的缓存中,调用update、saveOrUpdate等方法变为持久化状态,调用delete方法变为删除状态
- 删除状态:OID不为null,从一个Session实例的缓存中删除。被删除对象和数据库中的相关记录对应。Session已经计划将其从数据库中删除。Session在清理缓存时,会执行SQL delete语句,删除数据库中的相应记录
save和persist方法持久化对象区别
- save方法,无论id是否为null,save方法后会重新分配id,save方法调用后id不能修改
- persist方法,id必须为空,否则抛异常
StudentEntity entity = new StudentEntity();
entity.setName("test");
session.save(entity);
entity.setId(10000);
// 抛异常:
// javax.persistence.PersistenceException: org.hibernate.HibernateException: identifier of an instance of org.digdata.whf.entiy.StudentEntity was altered from 103 to 10000
get和load方法获取持久化对象区别
- 加载的时间:调用get方法会立即发送sql查询数据库,立即加载;load方法返回的是一个代理对象,只有当使用到该对象的属性时才会发送sql查询并且初始化对象,延迟加载
- 根据不存在的主键查询对象的处理:get方法会返回null,load方法直接抛出异常
- load方法可能会抛出LazyInitializationException异常,若session在使用代理对象先close掉,会抛出异常
- load()方法会使用二级缓存,而get()方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查找。在使用中,对使用了二级缓存的对象进行查询时最好使用load()方法,以充分利用二级缓存来提高检索的效率。
update方法、隐式更新、saveOrUpdate方法数据区别
- update方法适用于跨session更新,调用时一定会发送sql至数据库,当更新不存在的对象时,抛异常,可使用select-before-update=true,避免盲目发送sql请求,但是效率不高,不推荐使用
- 隐式更新依赖缓存,不适用跨session,当缓存中的数据与数据库的数据不一致时,才会发送sql至数据库
- saveOrUpdate:当id为null,执行save操作;当id不为null且与数据库主键对应,执行update操作;当id不为null且与数据库主键不对应,抛异常
delete方法后游离对象的id设置为null
use_identifier_rollback=true
hibernate+数据库触发器可能导致的问题
- hibernate的update方法默认会直接发送sql至数据库,若数据有update的触发器,会导致触发器也盲目的触发
- 若触发器修改了记录,session缓存无法感知到数据库数据的改变(flush+refresh)