hibernate批量操作实例详解
本文实例讲述了hibernate批量操作的方法。分享给大家供大家参考,具体如下:
hibernate的批量处理
hibernate完全以面向对象的方式来操作数据库,当程序里以面向对象的方式操作持久化对象时,将被自动转换为对数据库的操作。例如调用session的delete()方法来删除持久化对象,hibernate将负责删除对应的数据记录;当执行持久化对象的set方法时,hibernate将自动转换为对应的update方法,修改数据库的对应记录。
问题是如果需要同时更新100 000条记录,是不是要逐一加载100 000条记录,然后依次调用set方法——这样不仅繁琐,数据访问的性能也十分糟糕。对这种批量处理的场景,hibernate提供了批量处理的解决方案,下面分别从批量插入、批量更新和批量删除3个方面介绍如何面对这种批量处理的情形。
1 批量插入
如果需要将100 000条记录插入数据库,通常hibernate可能会采用如下做法:
session session = sessionfactory.opensession(); transaction tx = session.begintransaction(); for ( int i=0; i<100000; i++ ) { user u = new user (.....); session.save(customer); } tx.commit(); session.close();
但随着这个程序的运行,总会在某个时候运行失败,并且抛出outofmemoryexception(内存溢出异常)。这是因为hibernate的session持有一个必选的一级缓存,所有的user实例都将在session级别的缓存区进行了缓存的缘故。
为了解决这个问题,有个非常简单的思路:定时将session缓存的数据刷新入数据库,而不是一直在session级别缓存。可以考虑设计一个累加器,每保存一个user实例,累加器增加1。根据累加器的值决定是否需要将session缓存中的数据刷入数据库。
下面是增加100 000个user实例的代码片段:
private void testuser()throws exception { //打开session session session = hibernateutil.currentsession(); //开始事务 transaction tx = session.begintransaction(); //循环100 000次,插入100 000条记录 for (int i = 0 ; i < 1000000 ; i++ ) { //创建user实例 user u1 = new user(); u1.setname("xxxxx" + i); u1.setage(i); u1.setnationality("china"); //在session级别缓存user实例 session.save(u1); //每当累加器是20的倍数时,将session中的数据刷入数据库,并清空session缓存 if (i % 20 == 0) { session.flush(); session.clear(); tx.commit(); tx = session.begintransaction(); } } //提交事务 tx.commit(); //关闭事务 hibernateutil.closesession(); }
上面代码中,当i%20 == 0时,手动将session处的缓存数据写入数据库,并手动提交事务。如果不提交事务,数据将依然缓存在事务处——未进入数据库,也将引起内存溢出的异常。
这是对session级别缓存的处理,还应该通过如下配置来关闭sessionfactory的二级缓存。
hibernate.cache.use_second_level_cache false
注意:除了要手动清空session级别的缓存外,最好关闭sessionfactory级别的二级缓存。否则,即使手动清空session级别的缓存,但因为在sessionfactory级别还有缓存,也可能引发异常。
2 批量更新
上面介绍的方法同样适用于批量更新数据,如果需要返回多行数据,可以使用scroll()方法,从而可充分利用服务器端游标所带来的性能优势。下面是进行批量更新的代码片段:
private void testuser()throws exception { //打开session session session = hibernateutil.currentsession(); //开始事务 transaction tx = session.begintransaction(); //查询出user表中的所有记录 scrollableresults users = session.createquery("from user") .setcachemode(cachemode.ignore) .scroll(scrollmode.forward_only); int count=0; //遍历user表中的全部记录 while ( users.next() ) { user u = (user) users.get(0); u.setname("新用户名" + count); //当count为20的倍数时,将更新的结果从session中flush到数据库 if ( ++count % 20 == 0 ) { session.flush(); session.clear(); } } tx.commit(); hibernateutil.closesession(); }
通过这种方式,虽然可以执行批量更新,但效果非常不好。执行效率不高,而且需要先执行数据查询,然后再执行数据更新,并且这种更新将是逐行更新,即每更新一行记录,都需要执行一条update语句,性能非常低下。
为了避免这种情况,hibernate提供了一种类似于sql的批量更新和批量删除的hql语法。
3 sql风格的批量更新/删除
hibernate提供的hql语句也支持批量的update和delete语法。
批量update和delete语句的语法格式如下:
update | delete from? classname [where where_conditions]
关于上面的语法格式有以下四点值得注意:
● 在from子句中,from关键字是可选的。即完全可以不写from关键字。
● 在from子句中只能有一个类名,该类名不能有别名。
● 不能在批量hql语句中使用连接,显式的或隐式的都不行。但可以在where子句中使用子查询。
● 整个where子句是可选的。
假设,需要批量更改user类实例的name属性,可以采用如下代码片段完成:
private void testuser()throws exception { //打开session session session = hibernateutil.currentsession(); //开始事务 transaction tx = session.begintransaction(); //定义批量更新的hql语句 string hqlupdate = "update user set name = :newname"; //执行更新 int updatedentities = session.createquery( hqlupdate ) .setstring( "newname", "新名字" ) .executeupdate(); //提交事务 tx.commit(); hibernateutil.closesession(); }
从上面代码中可以看出,这种语法非常类似于preparedstatement的executeupdate语法。实际上,hql的这种批量更新就是直接借鉴了sql语法的update语句。
注意:使用这种批量更新语法时,通常只需要执行一次sql的update语句,就可以完成所有满足条件记录的更新。但也可能需要执行多条update语句,这是因为有继承映射等特殊情况,例如有一个person实例,它有customer的子类实例。当批量更新person实例时,也需要更新customer实例。如果采用joined-subclass或union-subclass映射策略,person和customer实例保存在不同的表中,因此可能需要多条update语句。
执行一个hql delete,同样使用 query.executeupdate() 方法,下面是一次删除上面全部记录的代码片段:
private void testuser()throws exception { //打开session实例 session session = hibernateutil.currentsession(); //开始事务 transaction tx = session.begintransaction(); //定义批量删除的hql语句 string hqlupdate = "delete user"; //执行批量删除 int updatedentities = session.createquery( hqlupdate ) .executeupdate(); //提交事务 tx.commit(); //关闭session hibernateutil.closesession(); }
由query.executeupdate()方法返回一个整型值,该值是受此操作影响的记录数量。实际上,hibernate的底层操作是通过jdbc完成的。因此,如果有批量的update或delete操作被转换成多条update或delete语句,该方法返回的是最后一条sql语句影响的记录行数。
希望本文所述对大家基于hibernate框架的java程序设计有所帮助。
上一篇: Java 线程池详解
下一篇: 基于PHP中自带的字符串操作函数合集