不懂技术的人或者技术新手往往容易被
“
框架
”
二字所唬住,所谓框架是前人对相关问题处理方案的总结,将对某类问题最有价值的解决方式汇集在一起,形成框架。其它人使用时,仅仅只需要按照框架缔结者设定的规则以及调用的
API
,来完成对框架的使用。
学习
hibernate
,学习的主要是
hibernate
的使用规则,理解这个框架的思想。
1
、
Configuration
、
SessionFactory
、
Session
三个类一个都不能少,通通都需要了解。所谓了解,其实是夸大了,不少人仅仅只是使用这三个类最简单的创建过程代码,但这已经足够应付绝大多数场景了。无论你使用的时
hibernate.properties
,还是
hibernate.cfg.xml
,抑或者你自定义了一个配置文件,确保自己配置的正确性。
2
、必须学习
hbm.xml
文件的编写规则。新手可以依靠一些自动生成工具来完成对
hbm.xml
以及
java
文件的生成,但上手便这样,不利于学习。工具虽然方便,但是掩盖了生成时所应该知道的基本原理。
3
、
hibernate
的三种查询方式:
HQL
、
Criteria
、
Native SQL
。每种技术有其应用的优势场景,技术不分优劣,只有最适合当前场景使用的。
HQL
是使用最频繁的,
Criteria
是完全
OO
的,当你需要使用特定数据库的特性时,
Native SQL
是首选。
4
、关联关系。数据库中的多表关联本是平常事,但这个问题又恰恰是最容易让人晕头转向的地方。
hibernate
处理关联关系的精髓内容在
hbm.xml
中,学习时(
1
)注意关联行为的主被动方(
2
)弄清关联的对象所依据的字段。
5
、优化。
Hibernate
在封装现有
JDBC
操作的同时,对数据库操作进行了默认的一些优化。而通过延迟加载与批量操作等相关参数设置,我们可以进一步对数据库操作的性能进行优化。
Hibernate
学习总结
1 Hibernate
概述
Hibernate 是一个持久层框架,用来负责实现对象和关系型数据库的转换。 2003 年 Hibernate 2 发布, 2005 年 Hibernate 3 发布。现在已经成为最为流行的 ORM ( Object/Relational Mapper )框架。
2 Hibernate 内容
2.1 Hibernate 的三种状态
A. Transient (临时对象)
new 出来的对象,此状态下的对象还没有与数据库的记录对应上。或者通过 session.delete(obj) 操作的 obj 对象将从 Detached 状态改变为 Transient 状态
B. Persistent (持久对象)
-- Transient 状态的对象使用 Session 的 save() 方法保存到数据库后,对象成为 persistent 状态,例:
DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) session.save(fritz);
--使用 Hibernate 从数据库得到数据并封装为对象(例如使用 get() 、 load() ),则该对象为 Persistent 状态;
>>get()
User user = (User) session.get(User.class, new Long(userID));
>>load()
User user = (User) session.load(User.class, new Long(userID));
get() 和 load() 的区别在于:当要查找的对象数据不存在时, load() 方法就是直接抛出异常,而 get() 方法则返回 null 值
-- Detached 状态对象重新和 session 关联后(通过 update 或 lock 方法)变成 Persistent 状态,例:
>>update()
//user 是 session1 关闭后留下的未关联对象
user.setPassword("secret");
Session session2 = sessionFactory.openSession();
Transaction tx = session2.beginTransaction();
session2.update(user); // 此时 user 从 Detached 状态转为 Persistent 状态( session 和 user 关联)
user.setUsername("jonny");
tx.commit();
session2.close();
这种方式,关联前后做修改都不打紧,关联前后做的修改都会被更新到数据库;
比如关联前你修改了 password ,关联后修改了 username ,事务提交时执行的 update 语句会把 password 、 username 都更新
>>lock()
Session session2 = sessions.openSession();
Transaction tx = session2 .beginTransaction();
session2 .lock(user, LockMode.NONE);
user.setPassword("secret");
user.setLoginName("jonny");
tx.commit();
session2 .close();
这种方式,关联前后是否做修改很重要,关联前做的修改不会被更新到数据库,
比如关联前你修改了 password ,关联后修改了 loginname ,事务提交时执行的 update 语句只会把 loginname 更新到数据库
所以,确信未关联对象没有做过更改才能使用 lock()
Hibernate 是一个持久层框架,用来负责实现对象和关系型数据库的转换。 2003 年 Hibernate 2 发布, 2005 年 Hibernate 3 发布。现在已经成为最为流行的 ORM ( Object/Relational Mapper )框架。
2 Hibernate 内容
2.1 Hibernate 的三种状态
A. Transient (临时对象)
new 出来的对象,此状态下的对象还没有与数据库的记录对应上。或者通过 session.delete(obj) 操作的 obj 对象将从 Detached 状态改变为 Transient 状态
B. Persistent (持久对象)
-- Transient 状态的对象使用 Session 的 save() 方法保存到数据库后,对象成为 persistent 状态,例:
DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) session.save(fritz);
--使用 Hibernate 从数据库得到数据并封装为对象(例如使用 get() 、 load() ),则该对象为 Persistent 状态;
>>get()
User user = (User) session.get(User.class, new Long(userID));
>>load()
User user = (User) session.load(User.class, new Long(userID));
get() 和 load() 的区别在于:当要查找的对象数据不存在时, load() 方法就是直接抛出异常,而 get() 方法则返回 null 值
-- Detached 状态对象重新和 session 关联后(通过 update 或 lock 方法)变成 Persistent 状态,例:
>>update()
//user 是 session1 关闭后留下的未关联对象
user.setPassword("secret");
Session session2 = sessionFactory.openSession();
Transaction tx = session2.beginTransaction();
session2.update(user); // 此时 user 从 Detached 状态转为 Persistent 状态( session 和 user 关联)
user.setUsername("jonny");
tx.commit();
session2.close();
这种方式,关联前后做修改都不打紧,关联前后做的修改都会被更新到数据库;
比如关联前你修改了 password ,关联后修改了 username ,事务提交时执行的 update 语句会把 password 、 username 都更新
>>lock()
Session session2 = sessions.openSession();
Transaction tx = session2 .beginTransaction();
session2 .lock(user, LockMode.NONE);
user.setPassword("secret");
user.setLoginName("jonny");
tx.commit();
session2 .close();
这种方式,关联前后是否做修改很重要,关联前做的修改不会被更新到数据库,
比如关联前你修改了 password ,关联后修改了 loginname ,事务提交时执行的 update 语句只会把 loginname 更新到数据库
所以,确信未关联对象没有做过更改才能使用 lock()
Ps:
如果将
Session
实例关闭,则
Persistent
状态的对象会成为
Detached
状态。
C. Detached (未关联对象)
Detached 状态的对象,与数据库中的具体数据对应,但脱离 Session 实例的管理,例如:
在使用 load() 、 get() 方法查询到数据并封装为对象之后,将 Session 实例关闭,则对象由 Persistent 状态变为 Detached 状态。
Detached 状态的对象之任何属性变动,不会对数据库中的数据造成任何的影响。
这种状态的对象相当于 cache 数据,因为他不和 session 关联,谁都可以用,任何 session 都可以用它,用完后再放到 cache 中。
2.2 VO 和 PO
将 Transient 状态和 Detached 状态的对象统称为 VO ( Value Object )对象,而将 Persistent 状态的对象称为 PO ( Persistence Object )对象。
我认为,可以简单的理解为当前和 session 关联的对象就是 PO 对象,否则为 VO 对象
Ps: 应该尽量避免将 PO 对象传入 / 传出除持久层外的其他层面(如在 view 层修改 PO )。解决办法是构造一个新的 VO ,使其具备和 PO 相同的属性,在系统其他层面使用 VO 进行数据传输。(但在实际应用中这种方法增加了系统复杂性并且影响性能,所以很多系统设计还是倾向于可以在 view 层操纵 PO 的)
2.3 SessionFactory 和 Session
SessionFactory 用来创建 session 对象,由于 SessionFactory 本身是单例实现,并且是线程安全的,所以在一个应用中只能创建一个 SessionFactory 实例。例:
Configuration config = new Configuration.configure();
SessionFactory sessionFactory = config.buildSessionFactory();
C. Detached (未关联对象)
Detached 状态的对象,与数据库中的具体数据对应,但脱离 Session 实例的管理,例如:
在使用 load() 、 get() 方法查询到数据并封装为对象之后,将 Session 实例关闭,则对象由 Persistent 状态变为 Detached 状态。
Detached 状态的对象之任何属性变动,不会对数据库中的数据造成任何的影响。
这种状态的对象相当于 cache 数据,因为他不和 session 关联,谁都可以用,任何 session 都可以用它,用完后再放到 cache 中。
2.2 VO 和 PO
将 Transient 状态和 Detached 状态的对象统称为 VO ( Value Object )对象,而将 Persistent 状态的对象称为 PO ( Persistence Object )对象。
我认为,可以简单的理解为当前和 session 关联的对象就是 PO 对象,否则为 VO 对象
Ps: 应该尽量避免将 PO 对象传入 / 传出除持久层外的其他层面(如在 view 层修改 PO )。解决办法是构造一个新的 VO ,使其具备和 PO 相同的属性,在系统其他层面使用 VO 进行数据传输。(但在实际应用中这种方法增加了系统复杂性并且影响性能,所以很多系统设计还是倾向于可以在 view 层操纵 PO 的)
2.3 SessionFactory 和 Session
SessionFactory 用来创建 session 对象,由于 SessionFactory 本身是单例实现,并且是线程安全的,所以在一个应用中只能创建一个 SessionFactory 实例。例:
Configuration config = new Configuration.configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session
对象用
SessionFactory
创建,创建后可以调用其
save,get,delete,find
方法对持久层数据进行操作。(其中,
find()
方法在
Hibernate3
中被取消,用
Query
或
Criteria
代替)最后,通过调用
session.close()
方法关闭
session
。
2.4 缓存
缓存种类:分为事务级缓存,应用级缓存和分布式缓存。但由于分布式缓存在同步过程中会占用大量带宽,严重影响系统性能,所以不建议使用。
2.4.1 一级缓存
一级缓存是 Hibernate 的内部缓存,属于事务级缓存。
它在 SessionImpl 中,以 Map 的形式实现。当要从 Session 中取得某个 PO 时,会判断此 PO 的 id 和 ClassName 在 Map 中是否已经存在,如果已经存在,则直接从缓存中取出。
有两种手动干预内部缓存的方法:
a. Session.evict
将某个特定对象从内部缓存中清楚
b. Session.clear
清空内部缓存
2.4 缓存
缓存种类:分为事务级缓存,应用级缓存和分布式缓存。但由于分布式缓存在同步过程中会占用大量带宽,严重影响系统性能,所以不建议使用。
2.4.1 一级缓存
一级缓存是 Hibernate 的内部缓存,属于事务级缓存。
它在 SessionImpl 中,以 Map 的形式实现。当要从 Session 中取得某个 PO 时,会判断此 PO 的 id 和 ClassName 在 Map 中是否已经存在,如果已经存在,则直接从缓存中取出。
有两种手动干预内部缓存的方法:
a. Session.evict
将某个特定对象从内部缓存中清楚
b. Session.clear
清空内部缓存
当批量插入数据时,会引发内存溢出,这就是由于内部缓存造成的。例如:
For(int i=0; i<1000000; i++){
For(int j=0; j<1000000; j++){
User user = new User();
user.setUserName(“gaosong”);
user.setPassword(“123”);
session.save(user);
}
}
在每次循环时,都会有一个新的对象被纳入内部缓存中,所以大批量的插入数据会导致内存溢出。解决办法有两种: a 定量清除内部缓存 b 使用 JDBC 进行批量导入,绕过缓存机制。
For(int i=0; i<1000000; i++){
For(int j=0; j<1000000; j++){
User user = new User();
user.setUserName(“gaosong”);
user.setPassword(“123”);
session.save(user);
}
}
在每次循环时,都会有一个新的对象被纳入内部缓存中,所以大批量的插入数据会导致内存溢出。解决办法有两种: a 定量清除内部缓存 b 使用 JDBC 进行批量导入,绕过缓存机制。
a
定量清除内部缓存
For(int i=0; i<1000000; i++){
For(int j=0; j<1000000; j++){
User user = new User();
user.setUserName(“gaosong”);
user.setPassword(“123”);
session.save(user);
For(int i=0; i<1000000; i++){
For(int j=0; j<1000000; j++){
User user = new User();
user.setUserName(“gaosong”);
user.setPassword(“123”);
session.save(user);
if(j%50 == 0){
session.flush();
session.clear(); // 清除内部缓存的全部数据,及时释放出占用的内存
}
}
}
session.flush();
session.clear(); // 清除内部缓存的全部数据,及时释放出占用的内存
}
}
}
b
使用
JDBC
进行批量导入,绕过缓存机制
Transaction tx=session.beginTransaction(); // 使用 Hibernate 事务处理边界 Connection conn=session.connection();
PrepareStatement stmt=conn.prepareStatement(“insert into T_STUDENT(name) values(?)”);
for(int j=0;j++;j<200){
for(int i=0;i++;j<50) {
stmt.setString(1,”feifei”);
}
}
stmt.executeUpdate();
tx.commit(); // 使用 Hibernate 事务处理边界
Transaction tx=session.beginTransaction(); // 使用 Hibernate 事务处理边界 Connection conn=session.connection();
PrepareStatement stmt=conn.prepareStatement(“insert into T_STUDENT(name) values(?)”);
for(int j=0;j++;j<200){
for(int i=0;i++;j<50) {
stmt.setString(1,”feifei”);
}
}
stmt.executeUpdate();
tx.commit(); // 使用 Hibernate 事务处理边界
ps
:注意,这里使用
Hibernate
的事务处理机制处理
Connection
2.4.2 二级缓存
2.4.2.1 概述
Hibernate 的二级缓存涵盖了应用级缓存和分布式缓存领域。 Hibernate 默认使用 EHCache 作为二级缓存的实现。(二级缓存由第三方实现)
缓存后,可能出现问题主要有:
1. 脏读:一个事务读取了另一个并行事务未提交的数据。
2. 不可重复读:一个事务再次读取之前的数据时,得到的数据不一致,被另一个已提交的事务修改。
3. 幻想读:一个事务重新执行一个查询,返回的记录中包含了因为其他最近提交的事务而产生的新记录。
对应以上问题, Hibernate 提供了四种内置的缓存同步策略:
1. read-only :只读。对于不会发生改变的数据,可使用只读型缓存。
2. nonstrict-read-write :如果程序对并发访问下的数据同步要求不是非常严格,且数据更新操作频率较低,可以采用本选项,获得较好的性能。
3. read-write :严格可读写缓存。基于时间戳判定机制,实现了 “read committed” 事务隔离等级。可用于对数据同步要求严格的情况,但不支持分布式缓存。这也是实际应用中使用最多的同步策略。
4. transactional :事务型缓存,发生异常的时候,缓存也能够回滚,必须运行在 JTA 事务环境中。实现了 “Repeatable read” 事务隔离等级。
2.4.2 二级缓存
2.4.2.1 概述
Hibernate 的二级缓存涵盖了应用级缓存和分布式缓存领域。 Hibernate 默认使用 EHCache 作为二级缓存的实现。(二级缓存由第三方实现)
缓存后,可能出现问题主要有:
1. 脏读:一个事务读取了另一个并行事务未提交的数据。
2. 不可重复读:一个事务再次读取之前的数据时,得到的数据不一致,被另一个已提交的事务修改。
3. 幻想读:一个事务重新执行一个查询,返回的记录中包含了因为其他最近提交的事务而产生的新记录。
对应以上问题, Hibernate 提供了四种内置的缓存同步策略:
1. read-only :只读。对于不会发生改变的数据,可使用只读型缓存。
2. nonstrict-read-write :如果程序对并发访问下的数据同步要求不是非常严格,且数据更新操作频率较低,可以采用本选项,获得较好的性能。
3. read-write :严格可读写缓存。基于时间戳判定机制,实现了 “read committed” 事务隔离等级。可用于对数据同步要求严格的情况,但不支持分布式缓存。这也是实际应用中使用最多的同步策略。
4. transactional :事务型缓存,发生异常的时候,缓存也能够回滚,必须运行在 JTA 事务环境中。实现了 “Repeatable read” 事务隔离等级。
Ps:
读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁,其他事务如果去取相应的缓存数据,发现被锁住了,然后就直接取数据库查询。
在 hibernate2.1 的 ehcache 实现中,如果锁住部分缓存的事务发生了异常,那么缓存会一直被锁住,直到 60 秒后超时。
不严格读写缓存不锁定缓存中的数据。
2.4.2.2 锁
为了避免脏数据的出现, Hibernate 默认实现了乐观锁机制。原理是为表加一个 version 字段来控制。例如: version 初始值为 1 , A 和 B 两个事务同时修改表, A 和 B 都读入了 version=1 。 A 修改完成后将 version 加 1 , version=2 。 B 修改完后也将 version 加 1 ,当试图提交时发现:当前 version 已经为 2 ,而提交版本也为 2 ,违反了 “ 提交版本必须大于记录版本 ” 的乐观锁策略,事务被回滚。因此避免了脏数据的出现的可能。
2.4.2.3 Class 缓存
Class 缓存是一个 Map ,其中 key 是 ClassName , Value 是一个 id 序列(其中的 id 就对应 DB 中的一条记录,也就是对应一个 PO )。当调用 session.iterate(…) 方法时,将通过一条查询语句返回一个 id 序列,只要序列中的 id 在 Map 中存在,则取出 Map 中此 id 对应的 POJO 。
session.iterate(…) 方法和 session.find(…) 方法的区别: session.find(…) 方法并不读取 ClassCache ,它通过查询语句直接查询出结果数据,并将结果数据 put 进 classCache ; session.iterate(…) 方法返回 id 序列,根据 id 读取 ClassCache ,如果没有命中在去 DB 中查询出对应数据。
当使用 session.find(…) 方法时,如果返回大量数据,会将很多 POJO 放入内存中,导致内存溢出,此时可以用 limit 方式限定返回数量。
Ps: 由于 session.iterate(…) 方法不能规避 N+1 的问题,所以基本不用此方法。
2.4.2.4 查询缓存
Hibernate 查询缓存根据 HQL 生成的 SQL 作为 key ,对应一个查询出的 id 序列,如果两次查询的 SQL 相同,将命中查询缓存,根据 id 序列再到 ClassCache 中 load 出对应的 POJO 。但是,如果在两次查询中数据库表有改动( Update/Insert/Delete ),则会将查询缓存中的数据清空。所以查询缓存只能应用于两次查询中 DB 表没有改变,并且是同样的 SQL 查询的情况。
这里有个问题,就是查询缓存机制可能会遇到 N+1 的问题。因为当从查询缓存中读出 id 序列后,将去 ClassCache 中找,如果此时 ClassCache 中没有 id 对应的 POJO ,则将向数据库中发送查询语句。所以一定要确保 ClassCache 中数据的生命周期要比 QueryCache 的长,(比如 ClassCache 的超时时间一定不能短于 QueryCache 设置的超时时间)
2.4.2.5 批量操作( Update/Delete )
在 Hibernate3.0.5 以前的版本和以后的有很大差异,这里分两部分祥解:
1. Hibernate2.x
在 Hibernate2.x 中,批量操作时是不推荐用 Hibernate 自己的 update/delete 方法的,这是因为 Hibernate 在批量更新 / 删除时必须维护 ClassCache ,这就导致出现 N+1 的情况影响性能,所以不可取。解决方法是使用 JDBC 的相应方法,绕开缓存。
2. Hibernate3.x
在 Hibernate3.x 中,加入了 “bulk update/delete” 的操作,使得可以使用 Hibernate 本身的批量更新功能。
“bulk update” 只是简单的发送一条 update 语句,并不维护 ClassCache 。此时就会有脏数据出现的可能。(需要确认)
另一方面, “bulk delete” 发送一条 delete 语句,并且到 ClassCache 中将 delete 对应的 Class 设置为无效,实际是清空了表对应的 ClassCache 。
2.4.3 session.save
session.save 方法的执行顺序:
1. 在一级缓存中寻找 POJO ,如果命中则认为此对象执行过 Insert 方法,直接返回。
2. 如果实现了 lifecycle 接口,则调用 onSave 方法
3. 如果实现了 Validatable 接口,则调用 validate 方法
4. 调用拦截机的 Interceptor.onSave 方法
5. 构造 Insert Sql ,并且执行
6. 返回新插入记录的 id 给 POJO
7. 将 POJO 放入一级缓存中( session.save 不会将 POJO 放入二级缓存,是因为通过 save 保存的 POJO 在事务的剩余部分被修改的可能往往很高,频繁更改二级缓存得不偿失)
8. 处理级连关系
3 Hibernate3.2 新特性
1. Hibernate Annotation
2. bulk update/delete (批量更新 / 删除)
3. 和 lucene 集成
4. 取消 Hibernate2 中的 session.find 方法,改为 session.createQuery.list 方法
5. 修改包名
在 hibernate2.1 的 ehcache 实现中,如果锁住部分缓存的事务发生了异常,那么缓存会一直被锁住,直到 60 秒后超时。
不严格读写缓存不锁定缓存中的数据。
2.4.2.2 锁
为了避免脏数据的出现, Hibernate 默认实现了乐观锁机制。原理是为表加一个 version 字段来控制。例如: version 初始值为 1 , A 和 B 两个事务同时修改表, A 和 B 都读入了 version=1 。 A 修改完成后将 version 加 1 , version=2 。 B 修改完后也将 version 加 1 ,当试图提交时发现:当前 version 已经为 2 ,而提交版本也为 2 ,违反了 “ 提交版本必须大于记录版本 ” 的乐观锁策略,事务被回滚。因此避免了脏数据的出现的可能。
2.4.2.3 Class 缓存
Class 缓存是一个 Map ,其中 key 是 ClassName , Value 是一个 id 序列(其中的 id 就对应 DB 中的一条记录,也就是对应一个 PO )。当调用 session.iterate(…) 方法时,将通过一条查询语句返回一个 id 序列,只要序列中的 id 在 Map 中存在,则取出 Map 中此 id 对应的 POJO 。
session.iterate(…) 方法和 session.find(…) 方法的区别: session.find(…) 方法并不读取 ClassCache ,它通过查询语句直接查询出结果数据,并将结果数据 put 进 classCache ; session.iterate(…) 方法返回 id 序列,根据 id 读取 ClassCache ,如果没有命中在去 DB 中查询出对应数据。
当使用 session.find(…) 方法时,如果返回大量数据,会将很多 POJO 放入内存中,导致内存溢出,此时可以用 limit 方式限定返回数量。
Ps: 由于 session.iterate(…) 方法不能规避 N+1 的问题,所以基本不用此方法。
2.4.2.4 查询缓存
Hibernate 查询缓存根据 HQL 生成的 SQL 作为 key ,对应一个查询出的 id 序列,如果两次查询的 SQL 相同,将命中查询缓存,根据 id 序列再到 ClassCache 中 load 出对应的 POJO 。但是,如果在两次查询中数据库表有改动( Update/Insert/Delete ),则会将查询缓存中的数据清空。所以查询缓存只能应用于两次查询中 DB 表没有改变,并且是同样的 SQL 查询的情况。
这里有个问题,就是查询缓存机制可能会遇到 N+1 的问题。因为当从查询缓存中读出 id 序列后,将去 ClassCache 中找,如果此时 ClassCache 中没有 id 对应的 POJO ,则将向数据库中发送查询语句。所以一定要确保 ClassCache 中数据的生命周期要比 QueryCache 的长,(比如 ClassCache 的超时时间一定不能短于 QueryCache 设置的超时时间)
2.4.2.5 批量操作( Update/Delete )
在 Hibernate3.0.5 以前的版本和以后的有很大差异,这里分两部分祥解:
1. Hibernate2.x
在 Hibernate2.x 中,批量操作时是不推荐用 Hibernate 自己的 update/delete 方法的,这是因为 Hibernate 在批量更新 / 删除时必须维护 ClassCache ,这就导致出现 N+1 的情况影响性能,所以不可取。解决方法是使用 JDBC 的相应方法,绕开缓存。
2. Hibernate3.x
在 Hibernate3.x 中,加入了 “bulk update/delete” 的操作,使得可以使用 Hibernate 本身的批量更新功能。
“bulk update” 只是简单的发送一条 update 语句,并不维护 ClassCache 。此时就会有脏数据出现的可能。(需要确认)
另一方面, “bulk delete” 发送一条 delete 语句,并且到 ClassCache 中将 delete 对应的 Class 设置为无效,实际是清空了表对应的 ClassCache 。
2.4.3 session.save
session.save 方法的执行顺序:
1. 在一级缓存中寻找 POJO ,如果命中则认为此对象执行过 Insert 方法,直接返回。
2. 如果实现了 lifecycle 接口,则调用 onSave 方法
3. 如果实现了 Validatable 接口,则调用 validate 方法
4. 调用拦截机的 Interceptor.onSave 方法
5. 构造 Insert Sql ,并且执行
6. 返回新插入记录的 id 给 POJO
7. 将 POJO 放入一级缓存中( session.save 不会将 POJO 放入二级缓存,是因为通过 save 保存的 POJO 在事务的剩余部分被修改的可能往往很高,频繁更改二级缓存得不偿失)
8. 处理级连关系
3 Hibernate3.2 新特性
1. Hibernate Annotation
2. bulk update/delete (批量更新 / 删除)
3. 和 lucene 集成
4. 取消 Hibernate2 中的 session.find 方法,改为 session.createQuery.list 方法
5. 修改包名