105、Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法有什么区别?
答:Hibernate的对象有三种状态:瞬态、持久态和游离态。游离状态的实例可以通过调用save()、persist()或者saveOrUpdate()方法进行持久化;脱管状态的实例可以通过调用 update()、0saveOrUpdate()、lock()或者replicate()进行持久化。save()和persist()将会引发SQL的INSERT语句,而update()或merge()会引发UPDATE语句。save()和update()的区别在于一个是将瞬态对象变成持久态,一个是将游离态对象变为持久态。merge方法可以完成save()和update()方法的功能,它的意图是将新的状态合并到已有的持久化对象上或创建新的持久化对象。按照官方文档的说明:(1)persist()方法把一个瞬态的实例持久化,但是并"不保证"标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间;(2) persist"保证",当它在一个事务外部被调用的时候并不触发一个Insert语句,当需要封装一个长会话流程的时候,一个persist这样的函数是需要的。(3)save"不保证"第2条,它要返回标识符,所以它会立即执行Insert语句,不管是不是在事务内部还是外部。update()方法是把一个已经更改过的脱管状态的对象变成持久状态;lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。
106、阐述Session加载实体对象的过程。
答:Session加载实体对象的步骤是:
1) Session在调用数据库查询功能之前, 首先会在缓存中进行查询, 在一级缓存中, 通过实体类型和主键进行查找, 如果一级缓存查找命中且数据状态合法, 则直接返回
2) 如果一级缓存没有命中, 接下来Session会在当前NonExists记录(相当于一个查询黑名单, 如果出现重复的无效查询可以迅速判断, 从而提升性能)中进行查找, 如果NonExists中存在同样的查询条件,则返回null
3) 对于load方法, 如果一级缓存查询失败则查询二级缓存, 如果二级缓存命中则直接返回
4) 如果之前的查询都未命中, 则发出SQL语句, 如果查询未发现对应记录则将此次查询添加到Session的NonExists中加以记录, 并返回null
5) 根据映射配置和SQL语句得到ResultSet,并创建对应的实体对象
6) 将对象纳入Session(一级缓存)管理
7) 执行拦截器的onLoad方法(如果有对应的拦截器)
8) 将数据对象纳入二级缓存
9) 返回数据对象
107、Query接口的list方法和iterate方法有什么区别?
答:
1) list方法无法利用缓存,它对缓存只写不读; iterate方法可以充分利用缓存, 如果目标数据只读或者读取频繁, iterate可以减少性能开销
2) list方法不会引起N+1查询问题, 而iterate方法会引起N+1查询问题
108、Hibernate如何实现分页查询?
答:通过Hibernate实现分页查询,开发人员只需要提供HQL语句、查询起始行数(setFirstresult()方法)和最大查询行数(setMaxResult()方法),并调用Query接口的list()方法,Hibernate会自动生成分页查询的SQL语句。
109、锁机制有什么用?简述Hibernate的悲观锁和乐观锁机制。
答:有些业务逻辑在执行过程中往往需要保证数据访问的排他性,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。
Hibernate支持悲观锁和乐观锁两种锁机制。悲观锁,顾名思义,它悲观的认为在数据处理过程中一定存在修改数据的并发事务(包括本系统的其他事务或来自外部系统的事务),于是将处理的数据设置为锁定状态。悲观锁必须依赖数据库本身的锁机制才能真正保证数据访问的排他性。乐观锁,顾名思义,对并发事务持乐观态度(认为对数据的并发操作很少发生),通过更加宽松的锁机制解决悲观锁排他的数据访问对系统性能造成的严重影响。最常见的乐观锁是通过数据版本标识来实现的,读取数据时获得数据的版本号,更新数据时将此版本号加1,然后和数据库表对应记录的当前版本号进行比较,如果提交的数据版本号大于数据库中此记录的当前版本号则更新数据,否则认为是过期数据。
110、阐述实体对象的三种状态以及转换关系。
答:Hibernate中对象有三种状态:临时态(transient)、持久态(persistent)和游状态(detached),如下图所示。

图 Hibernate实体状态转换图
ü 临时状态:当new一个实体对象后,这个对象处于临时状态,即这个对象只是一个保存临时数据的内存区域,如果没有变量引用这个对象,则会被JVM的垃圾回收机制回收。这个对象所保存的数据与数据库没有任何关系,除非通过Session的save或者saveOrUpdate把临时对象与数据库关联,并把数据插入或者更新到数据库,这个对象才转换为持久对象。
ü 持久状态:持久化对象的实例在数据库中有对应的记录,并拥有一个持久化标识。对持久化对象进行delete操作后,数据库中对应的记录将被删除,那么持久化对象与数据库记录不再存在对应关系,持久化对象变成临时状态。持久化对象被修改变更后,不会马上同步到数据库,直到数据库事务提交。
ü 游离状态:当Session进行了close、clear或者evict后,持久化对象虽然拥有持久化标识符和与数据库对应记录一致的值,但是因为会话已经消失,对象不在持久化管理之内,所以处于游离状态(也叫脱管状态)。游离状态的对象与临时状态对象是十分相似的,只是它还含有持久化标识。
111、如何理解Hibernate的延迟加载机制。在实际应用中,延迟加载与session关闭的矛盾是如何处理的?
答:延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载。Hibernate使用了虚拟代理机制实现延迟加载。返回给用户的并不是实体本身,而是实体对象的代理。代理对象在用户调用getter方法时就会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时,数据库连接就同时关闭了。
延迟加载与session关闭的矛盾一般可以这样处理:
1) 关闭延迟加载特性。这种方式操作起来比较简单,因为hibernate的延迟加载特性是可以通过映射文件或者注解进行配置的,但这种解决方案存在明显的缺陷。首先,出现no session or session wasclosed就证明了系统中已经存在主外键关联,如果去掉延迟加载的话,则每次查询的开销都会变得很大。
2) 在session关闭之前先获取需要查询的数据(Hibernate.initialize()方法)。
3) 使用拦截器(Interceptor)或过滤器(Filter)控制Session。
112、举一个多对