• Hibernate 批处理(batch inserts, updates and deletes)


    总结:hibernate在进行批量处理不给力的主要原因就是Session中存在缓存,而hibernate的机制就是通过session中的一级缓存去同步数据库,所以当进行批量处理时,缓存中保存的数据量很大时会消耗很大内存资源,造成各种崩溃。

      其实平时工作中用到大量的批处理数据还是挺少的,很少遇到有上万条数据的批处理的,但是要是遇到了用hibernate去处理还是很纠结的,然后就去Hibernate官网看了看有没有啥处理方法学习下。

      刚工作的时候用hibernate还不是很熟练,遇到批处理的时候就直接切到JDBC做处理,比较混搭的风格。但是感觉看上去代码风格就不是很好的感觉。

      批量插入:

      如果有10w条数据需要插入到数据库中用hibernate做起来可能会比较困难。

      比较too young too naive的做法就是循环:

    复制代码
    1 Session session = sessionFactory.openSession();
    2 Transaction tx = session.beginTransaction();
    3 for ( int i=0; i<100000; i++ ) {
    4     Customer customer = new Customer(.....);
    5     session.save(customer);
    6 }
    7 tx.commit();
    8 session.close();
    复制代码

      这样做法大概会在5w条数据时候跳出OutOfMemoryExcepiton,因为hibernate的一级缓存原因,会把数据都先缓存到session中,等事务commit或者flush的时候才会把数据同步到数据库中,数据量太大时候session就hold不住了你懂的。

      可以有个小聪明的做法,用下面方法之前,记得开启JDBC的批处理,在hibernate配置文件中设置hibernate.jdbc.batch_size属性值在10到50之间

    复制代码
     1 Session session = sessionFactory.openSession();
     2 Transaction tx = session.beginTransaction();
     3    
     4 for ( int i=0; i<100000; i++ ) {
     5     Customer customer = new Customer(.....);
     6     session.save(customer);
     7     if ( i % 20 == 0 ) { //20, 和配置文件中hibernate.jdbc.batch_size属性值一致
     8         
     9         session.flush();  //强制刷新session中的缓存到数据库中
    10         session.clear();  //清除session中的缓存,ps:evict()干掉session中一个实例的缓存
    11     }
    12 }
    13    
    14 tx.commit();
    15 session.close();
    复制代码

      批量更新:

      批量更新的时候也可以利用fulsh()和clear()方法来定期的清楚session中的缓存,方法和上面的批量插入一样。除此之外,还可以用scroll()方法来处理当你想从数据库查询大量数据并想更新这些数据的时候,貌似有点游标的感觉啊:

      

    复制代码
     1 Session session = sessionFactory.openSession();
     2 Transaction tx = session.beginTransaction();
     3    
     4 ScrollableResults customers = session.getNamedQuery("GetCustomers")
     5     .setCacheMode(CacheMode.IGNORE)  // 设置cache模式为:这个session不会和cache有任何联系,不使用cache
     6     .scroll(ScrollMode.FORWARD_ONLY); 
     7 int count=0;
     8 while ( customers.next() ) {
     9     Customer customer = (Customer) customers.get(0);
    10     customer.updateStuff(...);
    11     if ( ++count % 20 == 0 ) {
    12         //同步数据并且释放内存:
    13         session.flush();
    14         session.clear();
    15     }
    16 }
    17    
    18 tx.commit();
    19 session.close();
    org.hibernate.CacheMode的常量字段有:
      GET    session会从缓存中读取数据,但是不会把数据增加到缓存中,除非把缓存中的数据更新为无效数据时
      IGNORE  session不和任何缓存有交互操作,除非把缓存中的数据更新为无效数据时
      PUT    session不会从缓存中读取数据,但是会把从数据库中读取的数据增加到缓存中
      REFRESH  session不从缓存中读取数据,但是会把从数据库读取的数据增加到缓存中,和PUT不同的是会忽略配置文件中的hibernate.cache.use_minimal_puts属性,就是为了强制刷新缓存

     org.hibernate.ScrollMode的常量字段有:

      FORWARD_ONLY: 请求一个类似游标的结果集,并且只转发这个结果集

      SCROLL_INSENSITIVE:请求一个游标的结果集,并且对基础数据的变化不敏感

      SCROLL_SENSITIVE:请求一个游标的结果集,并且对基础数据变化敏感

      没有怎么用过这个属性,不怎么清楚是什么意思。以后有空会再研究下补充上来

    复制代码

    通过StatelessSession来进行批处理
    复制代码
     1 StatelessSession session = sessionFactory.openStatelessSession();
     2 Transaction tx = session.beginTransaction();
     3    
     4 ScrollableResults customers = session.getNamedQuery("GetCustomers")
     5     .scroll(ScrollMode.FORWARD_ONLY);
     6 while ( customers.next() ) {
     7     Customer customer = (Customer) customers.get(0);
     8     customer.updateStuff(...); //做一些更新操作
     9     session.update(customer);
    10 }
    11    
    12 tx.commit();
    13 session.close();
    Notes:通过StatelessSession查询返回的Customer的实例立刻会成为游离状态,不会关联到任何的持久化层的上下文中也不会和缓存有任何关联。因为StatelessSession本身也不包括一级缓存,所以就不用考虑有缓存溢出的问题。
    复制代码

      Notes:StatelessSession没有一级缓存,也不会和二级缓存和其他缓存有任何交互,不会隐式产生transaction更没有脏数据检查。

      StatelessSession是一个比较低级别的十分接近底层JDBC的抽象接口。它定义的insert(),update()和delete()的方法都是直接作用到数据库的数据中。和直接使用JDBC的SQL操作数据库效果一样,但是和Session接口中的save(),saveOrUpdate()还有delete()定义的操作有很大的不同。

    最后的方法是可以通过HQL进行数据库的批量操作。这个会在后面的文章中继续提到的。

      下面是官方的原文,请原谅我的盗版:

    http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch04.html

  • 相关阅读:
    opencv 基本使用
    opencv 无法使用 dll 动态链接库 UnsatisfiedLinkError java.library.path Can't find dependent libraries
    System.load(String filename)和System.loadLibrary(String libname)的区别
    easyui-combox(tagbox) 多选操作 显示为tagbox
    form表单下的button按钮会自动提交表单的问题
    深夜一次数据库执行SQL思考(怎么看执行报错信息)
    Spring Boot 使用Java代码创建Bean并注册到Spring中
    maven 打jar 被引用后 出现 cannot resolve symbol 错误 生成jar包形式代码文件组织格式 非springboot文件组织格式
    Spring 自动转配类 在类中使用@Bean 注解进行转配但是需要排除该类说明
    EasyUI datagrid columns 中 field 区分大小写
  • 原文地址:https://www.cnblogs.com/wnlja/p/4345351.html
Copyright © 2020-2023  润新知