Hibernate的批量处理

摘要:
Hibernate以面向对象的方式操作数据库。当程序员以面向对象的方式操作持久对象时,他们将自动转换为数据操作。为了面对这种批处理场景,Hibernate提供了一种批处理解决方案。这是因为Hibernate的会话具有所需的一级缓存,所有用户实例都将缓存在会话级缓存中。1光纤电缆。use_second_level_Cachefalse除了手动清除会话级缓存外,最好在SessionFactory级别关闭二级缓存。为了避免这种情况,Hibernate提供了与DML语句类似的批更新和批删除HQL语法。

  Hibernate完全以面向对象的方式操作数据库,当程序员以面向对象的方式操作持久化对象时,将自动转换为对数据的操作。例如我们Session的delete()方法,来删除持久化对象,Hibernate将负责删除对应的数据记录;当我们执行持久化对象的setter方法时,Hibernate将自动转换为底层的update语句,修改数据库的对应记录。

  问题是:如果我们需要同时更新100000条记录,是不是要逐一加载100000条记录,然后依次调用setter方法——这样不仅繁琐,数据访问的性能也十分糟糕。为了面对这种批量处理的场景,Hibernate提供了批量处理的解决方案。下面分别从批量插入,批量更新和批量删除三个方面介绍如何面对这种批量处理的情形。

  批量插入:

1 Session session = HibernateSessionFactory.getSession();
2 Transaction tx = session.beiginTransaction();
3 //循环100000次插入100000
4 for(int i=0; i<100000; i++){
5     User u = new User();
6     session.save(u);              
7 }
8 tx.commit();
9 session.close();

  但随着这个程序的运行,总会在某个时候运行失败,并且抛出OutOfMemoryException(内存溢出异常)。这是因为Hibernate的Session持有一个必选的一级缓存,所有的User实例都将在Session级别的缓存区进行缓存的缘故。

  为了解决这个问题,有个非常简单的思路:定时将Session缓存的数据刷入数据库,而不是一直在Session级别缓存。可以考虑设计一个累加器,每保存一个User实例,累加器增加1.根据累加器的值决定是否需要将Session缓存的数据刷入数据库。

  下面是增加100000个User实例的代码:

 1 //打开Session
 2 Session session = HibernateSessionFactory.getSession();
 3 //开始事务
 4 Transaction tx = session.beginTransaction();
 5 //循环100000次,插入100000条记录
 6 for(int i = 0; i < 100000; i++){
 7      User u = new User();
 8      u.setName("XXX");
 9      ....
10      //在session级别缓存Usr实例
11      session.save(u);
12      //每当累加器是20的倍数时,将Session中数据刷入数据库
13      if(i % 20 == 0){
14           session.fluah();
15           session.clear();
16     }            
17 }
18 //提交事务
19 tx.commit();
20 //关闭事务
21 session.close();

  上面的代码中 i % 20 == 0时,手动将Session处缓存的数据写入数据库,并且清空Session缓存里的数据。除了要对Session级别缓存进行处理外,换应该通过如下配置来关闭SessionFactory的二级缓存。

1 hibernate.cache.use_second_level_cache false

        除了手动清空Session级别的缓存外,最好关闭SessionFactory级别的二级缓存。否则,即使手动flush Session级别的缓存,但因为在SessionFactory还有二级缓存,也可能引发异常。

  

  批量更新

  上面的方法同样适用于批量更新数据,如果需要换回多行数据,应该使用scroll()方法,从未可以充分利用服务器游标带来的性能优势。下面是进行批量更新的代码片段。

 1         // 打开Session
 2         Session session = HibernateSessionFactory.getSession();
 3         // 开始事务
 4         Transaction tx = session.beginTransaction();
 5         // 查询User表中的所有记录
 6         ScrollableResults users = session.createQuery("from User")
 7                 .setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY);
 8         int count = 0;
 9         //遍历User表中所有的记录
10         while(users.next()){
11             User = (User)users.get(0);
12             u.setName("新用户名"+count);
13             //当count为20的倍数时
14             //将更新的结果从Session中flush到数据库
15             if(++count % 20 == 0){
16                 session.flush();
17                 session.clear();
18             }
19         }
20         tx.commit();
21         session.close();

  通过这种方式,虽然可以执行批量更新,但效果非常不好。执行效率不高,需要先执行数据查询,然后在执行数据更新,而且这种更新将是逐行更新,即没更新一行记录,都需要执行一条update语句,性能也非常低下。

  为了避免这种情况,Hibernate提供了一种类似于DML语句的批量更新,批量删除的HQL语法。

  

  DML 风格的批量更新/删除

  Hibernate提供的HQL也支持批量的UPDATE和DELETE语法

  批量 UPDATE 和 DELETE语句的语法格式如下:

     

1 UPDATE | DELETE FROM? <ClassName> [WHERE WHERE_CONDITIONS]

  关于上面的语法格式有如下4点值得注意:

  》在FROM字句中,FROM关键字是可选的,即完全可以不写FROM关键字。

  》在FROM字句中只能有一个类名,该类名不能有别名。

  》不能在批量HQL语句中使用连接,显示或者隐式都不可以。但可以在WHERE字句中使用子查询。

  》整个WHERE字句是可选的。WHERE字句的语法和HQL语句中WHERE字句的语法完全相同。

  假设对于上面需要批量更改User类实例的name属性,可以采用如下代码片段完成。

 1         // 打开Session
 2         Session session = HibernateSessionFactory.getSession();
 3         // 开始事务
 4         Transaction tx = session.beginTransaction();
 5         //定义批量更新的HQL语句
 6         String hqlUpdate = "update User set name = :newName";
 7         //执行更新
 8         int rows = session.createQuery(hqlUpdate).setString("newName", "新名字").executeUpdate();
 9         tx.commit();
10         session.close();

  从上面的代码中可以看出,这种语法非常类似于PreparedStatement中的executeUpdate()语法,实际上,HQL的这种批量更新就是直接借鉴了SQL语法的UPDATE语句。

  使用这种批量更新语法时,通常只需要执行一次SQL的UPDATE语句,就可以完成所有满足条件记录的更新。但有可能需要执行多条UPDATE语句,这是因为有继承映射等特殊情况,例如有一个Person实例,他有Customer子类实例。当批量更新Person实例时,也需要更新Customer实例。如果采用 joined-subclass或union-subclass映射策略时,Person和Customer实例保存在不同的表中,因此可能需要多条UPDATE语句。

  执行HQL DELETE ,同样适用Query.executeUpdate()方法,下面是一次删除上面全部记录的代码片段。

 1  1         // 打开Session
 2  2         Session session = HibernateSessionFactory.getSession();
 3  3         // 开始事务
 4  4         Transaction tx = session.beginTransaction();
 5  5         //定义批量删除的HQL语句
 6  6         String hqlDelete = "delete User";
 7  7         //执行删除
 8  8         int rows = session.createQuery(hqlDelete).executeUpdate();
 9  9         tx.commit();
10 10         session.close();

  Query.executeUpdate()方法返回一个整数值,改值时受此操作影响的记录数量。我们知道 Hibernate的底层操作实际上都是由JDBC完成的,因此,如果有批量的UPDATE或DELETE操作将被转换成多条UPDATE或DELETE语句,该方法只能返回最后一条SQL语句影响的记录行数。

  

免责声明:文章转载自《Hibernate的批量处理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇win10下安装cygwin全过程过滤驱动加密文件(代码)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

spring security session管理

单机 Session 管理:   本文Demo 基于 springboot 2.0.1版本.   spring security 中提供了很好的 session 配置管理。包括session 无效处理、session 并发控制、session过期等相应处理配置。   在 Security 的配置中我们重写了 protected void configure...

SQL Update

sql: Update a SET a.province=b.khprovince, a.city =b.khcity, a.county = b.khcouty, a.town = b.rkhcouty from 客户校对 as a ,dbo.行政区划 as b where khmc like '%'+b.khprovince+'%'...

OPENQUERY (TransactSQL)

对给定的链接服务器执行指定的传递查询。该服务器是 OLE DB 数据源。OPENQUERY 可以在查询的 FROM 子句中引用,就好象它是一个表名。OPENQUERY 也可以作为 INSERT、UPDATE 或 DELETE 语句的目标表进行引用。但这要取决于 OLE DB 访问接口的功能。尽管查询可能返回多个结果集,但是 OPENQUERY 只返回第一个...

SQL Server:触发器详解

SQL Server:触发器详解  1. 概述 2. 触发器的分类 3. Inserted和Deleted表 4. 触发器的执行过程 5. 创建触发器 6. 修改触发器: 7. 删除触发器: 8. 查看数据库中已有触发器: 9. “Instead of”相关示例: 10. “After”触发器 11. 参考资源 1. 概述 触发器是一种特殊的存储过程...

基于jssip的简单封装

import {UA as Agent, WebSocketInterface as Socket, debug} from 'jssip'; import EventEmitter from "./eventEmitter"; debug('JsSIP:RTCSession:DTMF'); export default class SipClient...

hibernate(三) 一对多映射关系

    序言        前面两节讲了hibernate的两个配置文件和hello world!。还有hibernate的一级缓存和三种状态,基本上hibernate就懂一点了,从这章起开始一个很重要的知识点,hibernate的关系映射。一对一、一对多、多对多的讲解。,希望你们能从中学到东西,我也从中巩固自己的知识         计划:       ...