当我们使用SQL直接在数据库里面操作是,批量更新一大批数据是很快捷的,比如
update Companys set BidPrice='1000' where Name='MS'
不管有多少条Name='MS'的记录都会被更新,而且直接作用于数据至少比较快。
但是当我们使用Hibernate的时候,事情就有点变化了,当我们必须批处理如以上的数据时,假定有10000条,那么我们必须先Query(..).List出10000个Company对象,而这些对象会马上加载到Hibernate的第一级缓存中,也就是Session缓存,成为持久化对象(PO),
在更新他们的时候:
加载大量的PO会消耗很多内存,更加可怕的是,在加载PO的时候,Hibernate会产生数据的快照以便用来生成最终的SQL语句,当commit的时候,就会根据快照和实际PO的属性值进行比较,一旦发现有变化的属性,就会生成一个SQL语句,换句话说上面这点代码不仅要占用10000个Company对象的内存,还要生成10000个SQL语句。
所以有人想出限制批量的数量,把Hibernate缓存限制到一个数量,比如20,也就是说缓存中只能存在20个PO,然后在上面的代码中加计数,一旦遍历的数量达到20,就Flush,这里就不贴出代码了,我想说明的是这样的方式是治标不治本,还是会产生20个SQL语句,另外10000个PO对象,只是分批次(每批20个)载入缓存和分批次产生20个SQL,一共500批,客户机的负载是小了,但是数据库方面让然不容乐观,另外就是网络通信还是10000次,这是一个很笨的做法。
另外如果有人开启了第二级缓存,那好,Hibernate会把在第一级缓存做的事情再做一次,结果重新发生了10000次更新操作,也就是总共20000次的操作。
有人想用StatelessSession来执行批量更新,因为至少PO不会进入缓存,都是游离状态的对象了,也就不会占用缓存,由于没有被缓存引用,那么只要这个游离的PO被引用=0,就会被JVM垃圾收集掉,至少很大部分不用占用了,但是也许问题更大,因为在确认一个属性是否跟新过了而产生一个对应的SQL前必须先查询数据库,因为这些对象都是游离对象,没有快照支持,所以必须在更新之前查询一下数据库,也就是说每更新一次就得查询一次,这下好了,如果批量跟新10000个,也就必须查询10000次,数据库的压力上来了!也是不太好的办法。
估计有人还会想出来,直接JDBC吧,那么我们就是失去了使用Hibernate的机会,也就享受不到它提供的各种优于直接使用JDBC的好处,另外在结构上就成了混合型,结构不整洁了。
那怎么办?其实直接用HQL就可以:
它不需要缓存,另外只生成一条SQL update语句,客户机内存和服务器的负载都很低,就和直接使用SQL一样,但是还是有些性能损失的,至少Hibernate必须根据方言把上面的HQL转换成能够执行的SQL,需要一定的CPU时间。但是这样少的损失是可以忍受的,毕竟就是一次计算的过程,即使二级缓存被打开,也不会产生什么副作用了。
但是也不并是说任何场合下使用HQL都很高效,比如取得一个Company对应的所有订单,如果用HQL就会马上为所有这些订单对象填充数据,如果用Company.getOrders()就会好的多,因为我们可以在hbm映射文件中将Company的lazy=true,这样就有了延迟加载的能力,也就是说,我们在调用Company,getOrders才开始加载数据,不用的时候不加载。
其实,批量更新,如果用Hibernate调用存储过程,就更加快捷,网络交通只是一存储过程的名称和参数,比写一个HQL更加简短有效!
update Companys set BidPrice='1000' where Name='MS'
不管有多少条Name='MS'的记录都会被更新,而且直接作用于数据至少比较快。
但是当我们使用Hibernate的时候,事情就有点变化了,当我们必须批处理如以上的数据时,假定有10000条,那么我们必须先Query(..).List出10000个Company对象,而这些对象会马上加载到Hibernate的第一级缓存中,也就是Session缓存,成为持久化对象(PO),
在更新他们的时候:
Iterator Compamys=session.createQuery("from Company where Name='MS'").list().iterator();
while(Companys.hasNext())
{
Company comany=(Company)Companys.next();
company.setBidPrice(1000);
}
tx.commit();
Session.close();
加载大量的PO会消耗很多内存,更加可怕的是,在加载PO的时候,Hibernate会产生数据的快照以便用来生成最终的SQL语句,当commit的时候,就会根据快照和实际PO的属性值进行比较,一旦发现有变化的属性,就会生成一个SQL语句,换句话说上面这点代码不仅要占用10000个Company对象的内存,还要生成10000个SQL语句。
所以有人想出限制批量的数量,把Hibernate缓存限制到一个数量,比如20,也就是说缓存中只能存在20个PO,然后在上面的代码中加计数,一旦遍历的数量达到20,就Flush,这里就不贴出代码了,我想说明的是这样的方式是治标不治本,还是会产生20个SQL语句,另外10000个PO对象,只是分批次(每批20个)载入缓存和分批次产生20个SQL,一共500批,客户机的负载是小了,但是数据库方面让然不容乐观,另外就是网络通信还是10000次,这是一个很笨的做法。
另外如果有人开启了第二级缓存,那好,Hibernate会把在第一级缓存做的事情再做一次,结果重新发生了10000次更新操作,也就是总共20000次的操作。
有人想用StatelessSession来执行批量更新,因为至少PO不会进入缓存,都是游离状态的对象了,也就不会占用缓存,由于没有被缓存引用,那么只要这个游离的PO被引用=0,就会被JVM垃圾收集掉,至少很大部分不用占用了,但是也许问题更大,因为在确认一个属性是否跟新过了而产生一个对应的SQL前必须先查询数据库,因为这些对象都是游离对象,没有快照支持,所以必须在更新之前查询一下数据库,也就是说每更新一次就得查询一次,这下好了,如果批量跟新10000个,也就必须查询10000次,数据库的压力上来了!也是不太好的办法。
估计有人还会想出来,直接JDBC吧,那么我们就是失去了使用Hibernate的机会,也就享受不到它提供的各种优于直接使用JDBC的好处,另外在结构上就成了混合型,结构不整洁了。
那怎么办?其实直接用HQL就可以:
String hqlStatement="update Company m set m.BiDPrice=:NewPrice where m.Name=:ComName";
int updatedEntities=session.createQuery(hqlStatement)
.setString("NewPrice","1000")
.setString("ComName","MS")
.executeUpdate();
tx.commit();
session.close();
它不需要缓存,另外只生成一条SQL update语句,客户机内存和服务器的负载都很低,就和直接使用SQL一样,但是还是有些性能损失的,至少Hibernate必须根据方言把上面的HQL转换成能够执行的SQL,需要一定的CPU时间。但是这样少的损失是可以忍受的,毕竟就是一次计算的过程,即使二级缓存被打开,也不会产生什么副作用了。
但是也不并是说任何场合下使用HQL都很高效,比如取得一个Company对应的所有订单,如果用HQL就会马上为所有这些订单对象填充数据,如果用Company.getOrders()就会好的多,因为我们可以在hbm映射文件中将Company的lazy=true,这样就有了延迟加载的能力,也就是说,我们在调用Company,getOrders才开始加载数据,不用的时候不加载。
其实,批量更新,如果用Hibernate调用存储过程,就更加快捷,网络交通只是一存储过程的名称和参数,比写一个HQL更加简短有效!