批量更新:
1)session => jdbc-bache-size=? 10至50间为佳
<property name="jdbc.batch_size">20</property><!-- Hibernate的配置文件中设置JDBC单次批量处理的数目,合理的取值通常为10到50之间 -->
// 测试
for (int i = 1; i <= 100; i++) {
// 对象
Depts depts = new Depts(new Long(i), "T" + i);
// 保存
session.save(depts);
// 判断
if (i % 20 == 0) {// 单次批量操作的数目为20
session.flush(); // 清理缓存,执行批量插入20条记录的SQL insert语句
session.clear(); // 清空缓存中的Depts对象
}
}
批量添加、更新、删除时需要适当的清理缓存。
首先需要关闭hibernate 二次缓存。
2)hql进行批量更新(HQL只支持insert into ... select ...形式的插入语句,而不支持"insert into ... values ... "形式的插入语句。)
String hql="update Dept set d.dname='IT'";
session.createQuery().executeUpdate();
String hql="insert into Depts(deptno,dname) select d.deptno,d.dname from Dept d";
3)jdbc来执行批量更新
// sql语句
final String sql = "update Depts d set d.dname='IT'";
//定义一个匿名类,实现了Work接口
Work work=new Work() {
@Override
public void execute(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(sql);
ps.execute();
}
};
//执行work
session.doWork(work);
以上批量更新方式有两个缺点:
占用大量内存,必须把1万个Customer对象先加载到内存,然后一一更新它们。
执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新1万个Customer对象,频繁地访问数据库,会大大降低应用的性能。
一般说来,应该尽可能避免在应用层进行批量操作,而应该在数据库层直接进行批量操作,例如直接在数据库中执行用于批量更新或删除的SQL语句,如果批量操作的逻辑比较复杂,则可以通过直接在数据库中运行的存储过程来完成批量操作。
并不是所有的数据库系统都支持存储过程。例如目前的MySQL就不支持存储过程,因此不能通过存储过程来进行批量更新或批量删除。
当然,在应用层也可以进行批量操作,主要有以下方式:
(1)通过Session来进行批量操作。
注意:
(a)需要在Hibernate的配置文件中设置JDBC单次批量处理的数目,合理的取值通常为10到50之间,例如:hibernate.jdbc.batch_size=20
(b)Hibernate的第二级缓存是关闭的,此外也可以在Hibernate的配置文件中通过如下方式显式关闭第二级缓存:hibernate.cache.use_second_level_cache=false
(3)通过HQL来进行批量操作。
HQL(Hibernate Query Language,Hibernate查询语言)不仅可以检索数据,
还可以用于进行批量更新、删除和插入数据。批量操作实际上直接在数据库中完成,所处理的数据不会被保存在Session的缓存中,因此不会占用内存空间。
(4)直接通过JDBC API来进行批量操作。
当通过JDBC API来执行SQL insert、update和delete语句时,SQL语句中涉及到的数据不会被加载到内存中,因此不会占用内存空间。
以下程序直接通过JDBC API来执行用于批量更新的SQL语句:
值得注意的是,在Hibernate3中,尽管Session的connection()方法还存在,但是已经被废弃,不提倡使用了,
不过Hibernate3提供了替代方案:org.hibernate.jdbc.Work接口表示直接通过JDBC API来访问数据库的操作,
Work接口的execute()方法用于执行直接通过JDBC API来访问数据库的
HIbernate 检索策略:
检索策略 运行机制
立即检索 立即加载与当前对象并联的对象,需要执行多条select语句 n+1条语句
延迟检索 不立即加载与当前对象关系关联的对象,在第一次访问关联对象时才加载其信息
迫切左外连接检索 通过左外连接加载与当前对象的关联对象为立即检索策略,但执行的select语句少,只执行1条select连接查询语句
查询: lazy="true" 延迟查询
load() 仅影响session的load()方法
lazy 属性默认为true 即进行延迟加载。
延迟查询:在实际调用查询结果时检索
立即查询:在调用load/get时检索
Session get() / load()
检索策略包括:
1)类级别 lazy = "true"
对于Session的检索方式,类级别检索策略仅适用于load方法 ;也就说,对于get、qurey检索,持久化对象都会被立即加载而不管lazy是false还是true。
一 般来说,我们检索对象就是要访问它,因此立即检索是通常的选择。由于load方法在检索不到对象时会抛出异常(立即检索的情况下),因此我个人并不建议使 用load检
索;而由于<class>中的lazy属性还影响到多对一及一对一的检索策略,因此使用load方法就更没必要了
2)关联级别 lazy="true"
有立即检索、延迟检索和迫切左外连接检索。
对于关联级别检索,又可分为一对多和多对多、多对一和一对一两种情况讨论。
一对多和多对多关联关系一般使用<set>配置。<set>有lazy和outer-join属性,它们的不同取值绝对了检索策略。
1)立即检索:这是一对多默认的检索策略,此时lazy=false,outer-join=false.尽管这是默认的检索策略,但如果关联的集合是无用的,那么就不要使用这种检索方式。
2)延迟检索:此时lazy=true,outer-join=false(outer-join=true是无意义的),这是优先考虑的检索方式。
3)迫切左外连接检索:此时 lazy=false,outer-join=true,这种检索策略只适用于依靠id检索方式(load、get),而不适用于query的集合检索 (它会采用立即检索策略)。相
比于立即检索,这种检索策略减少了一条sql语句,但在Hibernate中,只能有一个<set>配置成 outer-join=true.
多对一和一对一检索策略一般使用<many-to-one>、<one-to-one>配置。<many- to-one>中需要配置的属性是 outer-join,同时还需要配置one端关联的<class>的lazy属性
(配置的可不是<many-to-one>中 的lazy哦),它们的组合后的检索策略如下:
1) outer-join=auto:这是默认值,如果lazy=true为延迟检索,如果lazy=false为迫切左外连接检索。
2) outer-join=true,无关于lazy,都为迫切左外连接检索。
3) outer-join=false,如果lazy=true为延迟检索,否则为立即检索。
可以看到,在默认的情况下(outer-join=auto,lazy=false),对关联的one端对象Hibernate采用的迫切左外连接检索。 依我看,很多情况下,我们并不需要加载one端关联的
对象(很可能我们需要的仅仅是关联对象的id);另外,如果关联对象也采用了迫切左外连接检索,就会 出现select语句中有多个外连接表,如果个数多的话会影响检索性能,
这也是为什么Hibernate通过 hibernate.max_fetch_depth属性来控制外连接的深度。对于迫切左外连接检索,query的集合检索并不适用,它会采用立即检索策 略。
对于检索策略,需要根据实际情况进行选择。对于立即检索和延迟检索,它们的优点在于select语句简单(每张表一条语句)、查询速度快,缺点在于关联表 时需要多条select
语句,增加了访问数据库的频率。因此在选择即检索和延迟检索时,可以考虑使用批量检索策略来减少select语句的数量(配置 batch-size属性)。对于切左外连接检索,优点
在于select较少,但缺点是select语句的复杂度提高,多表之间的关联会是很耗时的操作。 另外,配置文件是死的,但程序是活的,可以根据需要在程序里显示的指定检索策略
(可能经常需要在程序中显示指定迫切左外连接检索)。为了清楚检索策略的配 置效果如何,可以配置show_sql属性查看程序运行时Hibernate执行的sql语句。
详细可查看:点击打开链接