Hibernate4性能之批量处理的三种方式

假如有如下程序,需要向数据库里面加如100000条数据
Session session = sessionFactory.openSession();  
Transaction tx = session.beginTransaction();  
for ( int i=0; i<100000; i++ ) {  
    Customer customer = new Customer(.....);  
    session.save(customer);
}  
tx.commit();  
session.close();

这段程序大概运行到50000条记录左右会失败并抛出内存溢出异常(OutOfMemoryException),按照前面讲过的原理,新产生的object瞬时对象变为持久对象后,在session关闭之前将自动装载到session级别的缓存区,如果,程序使用了二级缓存,同样也会装入到二级缓存。所以当数据量大时,导致内存溢出。

解决方案:
设置批量抓取数量参数设置到一个合适值(比如10-50之间),同时最好关闭二级缓存,如果有的话。

<property name="hibernate.jdbc.batch_size">20</property>
<property name="hibernate.cache.use_second_level_cache">false</property>

但是,这不是绝对必须的,因为我们可以显式设置CacheMode来关闭与二级缓存的交互。
如果要将很多对象持久化,你必须通过经常的调用 flush() 以及稍后调用 clear() 来控制第一级缓存的大小。

Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
//将本批数据插入数据库,并释放内存
for ( int i=0; i<100000; i++ ) {  
   Customer customer = new Customer();  
   session.save(customer);  
   if ( i % 20 == 0 ) { //20, same as the JDBC batch size
      session.flush();  
      session.clear();  
   }  
}
tx.commit();
session.close();



Hibernate批量操作的三种方法:

方法一(直接用HQL语句):
由Query.executeUpdate()方法返回的整型值表明了受此操作影响的记录数量。注意这个数值可能与数据库中被(最后一条SQL语句)影响了的“行”数有关,也可能没有。一个大批量HQL操作可能导致多条实际的SQL语句被执行, 举个例子,对joined-subclass映射方式的类进行的此类操作。这个返回值代表了实际被语句影响了的记录数量。在那个joined-subclass的例子中, 对一个子类的删除实际上可能不仅仅会删除子类映射到的表而且会影响“根”表,还有可能影响与之有继承关系的joined-subclass映射方式的子类的表。

INSERT语句的伪码是: INSERT INTO EntityName properties_list select_statement.
要注意的是:只支持INSERT INTO ... SELECT ...形式,不支持INSERT INTO ... VALUES ...形式.

Session session=SessionFactory.openSession();
    Transaction tx=session.beginTransaction();
    String hql = "update Customer set phone=Trim(phone)",
    //String hql = "delete Customer where id < 5000",
    //String hql = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
    int createdEntities = s.createQuery(hql).executeUpdate();
    tx.commit();
    session.close();



方法二(利用服务器游标):
此方法同样适用于检索和更新数据。此外,在进行会返回很多行数据的查询时, 你需要使用 scroll()方法以便充分利用服务器端游标所带来的好处。

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//关掉缓存策略
ScrollableResults customers = session.getNamedQuery("GetCustomers")
                              .scroll(ScrollMode.FORWARD_ONLY)
                              .setCacheMode(CacheMode.IGNORE);
while ( customers.next() ) {  
   Customer customer = (Customer) customers.get(0);  
   customer.updateStuff(...);  
   session.update(customer);
} 
tx.commit();
session.close();



方法三(无状态session接口):
StatelessSession没有持久化上下文,也不提供多少高层的生命周期语义。特别是,无状态session不实现第一级cache,也不和第二级缓存,或者查询缓存交互。它不实现事务化写,也不实现脏数据检查。用stateless session进行的操作甚至不级联到关联实例。stateless session忽略集合类(Collections)。通过stateless session进行的操作不触发Hibernate的事件模型和拦截器。无状态session对数据的混淆现象免疫,因为它没有第一级缓存。无状态session是低层的抽象,和低层JDBC相当接近。

在下面的例子中,查询返回的Customer实例立即被脱管(detach)。它们与任何持久化上下文都没有关系。
StatelessSession 接口定义的insert(),update()和delete()操作是直接的数据库行级别操作,其结果是立刻执行一条INSERT, UPDATE 或 DELETE 语句。因此,它们的语义和Session 接口定义的save(),saveOrUpdate()和delete()操作有很大的不同。
StatelessSession不参与hibernate的各种缓存,默认的隔离级别对他也不起作用,直接生成sql直接执行,类似jdbc。但是他需要自己去写事务提交,spring好像也拦截不到它
如果batch设置成100的话,那么会每一百次执行一次同步一次数据库
有一个注意的地方:如果表中的主键是identity的话,那么batch就会失效,直接用jdbc也是一样失效

StatelessSession session = sessionFactory.openStatelessSession();  
Transaction tx = session.beginTransaction();
ScrollableResults customers = session.getNamedQuery("GetCustomers")
                              .scroll(ScrollMode.FORWARD_ONLY);
while ( customers.next() ) {  
   Customer customer = (Customer) customers.get(0);  
   customer.updateStuff(...);  
   session.update(customer);  
} 
tx.commit();
session.close();

你可能感兴趣的:(Hibernate,批量处理)