Hibernate批量操作(JDBC批量操作)

部分内容转自 :http://gaolixu.iteye.com/blog/519086

部分内容转自 :http://superjavason.iteye.com/blog/255423

 

--------------------------------------------------------------------------------------------

 

hibernate.jdbc.fetch_size 50

hibernate.jdbc.batch_size 25

这面这两项属性很重要

 

配置方法如下:

<session-factory>
    ...
    <property name="hibernate.jdbc.batch_size">50</property>
    ...
</session.factory>

 

--------------------------------------------------

 

Fetch Size (抓取大小):

是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。

一般我们查询时会返回一个ResultSet对象,他其实是一个数据库的游标,要时刻保持与数据库的连接,不可断开。 

例如一次查询结果是1万条记录,对于Oracle的JDBC驱动来说,是不会1次性把1万条结果全返出来的,而只会返出Fetch Size数量的记录,当不够用时,再去数据库取Fetch Size条数据,当然这一过程你是完全感觉不出来的。

 

 

Fetch Size设的越大,读数据库的次数越少,速度越快,越耗内存;

Fetch Size设的越小,读数据库的次数越多,速度越慢,前期会省内存(后期1万条都读出来了还是要用内存的)。

 

Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个非常保守的设定,根据我的测试,当Fetch Size=50的时候,性能会提升1倍之多,当Fetch Size=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了。

因此我建议使用Oracle的一定要将Fetch Size设到50。

 

不过并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持。

 

MySQL就像我上面说的那种最坏的情况,他总是一下就把1万条记录完全取出来,内存消耗会非常非常惊人!这个情况就没有什么好办法了 :(

 

---------------------------------------------------------------------------

Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点相当于设置Buffer缓冲区大小的意思。

 

Batch Size越大,批量操作的向数据库发送sql的次数越少,速度就越快。我做的一个测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch Size = 50的时候,删除仅仅需要5秒!!!

 

这有点像平时我们写程序写硬盘文件一样,设立一个Buffer,每次写入Buffer,等Buffer满了以后,一次写入硬盘,道理相同。

 

-----------------------------------------------------------------------------

hibernate.max_fetch_depth  设置外连接抓取树的最大深度取值. 建议设置为03之间

就是每次你在查询时,会级联查询的深度,譬如你对关联vo设置了eager的话,如果fetch_depth值太小的话,会发多很多条sql

-----------------------------------------------------------------------------

1 伪批量删除

Java代码 复制代码  收藏代码
  1. public void delete(final List<Integer> ids)   
  2. {   
  3.     final Session session = hibernateUtil.getCS();   
  4.     final Query q = session.createQuery("delete from Img where userId=:userId and id in(:ids)");   
  5.     final User user = (User) ActionContext.getContext().getSession().get("s");   
  6.     q.setInteger("userId", user.getId());   
  7.     q.setParameterList("ids", ids);   
  8.     q.executeUpdate();   
  9. }  
	public void delete(final List<Integer> ids)
	{
		final Session session = hibernateUtil.getCS();
		final Query q = session.createQuery("delete from Img where userId=:userId and id in(:ids)");
		final User user = (User) ActionContext.getContext().getSession().get("s");
		q.setInteger("userId", user.getId());
		q.setParameterList("ids", ids);
		q.executeUpdate();
	}

 

 2 批量插入

大家说下面的代码,50次flush一下, 如果同时也设置了<property name="hibernate.jdbc.batch_size">40(或其它值)</property>会怎么样呢?

 

Java代码 复制代码  收藏代码
  1. Session session = sessionFactory.openSession();    
  2. Transaction tx = session.beginTransaction();      
  3. for ( int i=0; i<100000; i++ ) {    
  4.     Customer customer = new Customer(.....);    
  5.     //如果你的 hibernate.cache.use_second_level_cache 是 true, 请在会话级别上关闭他       
  6.     //向(任何一级)缓存中加载大量数据通常也意味着它们很快会被清除出去,这会增加GC开销。     
  7.     session.setCacheMode(CacheMode.IGNORE);   
  8.     session.save(customer);    
  9.     if ( i % 50 == 0 ) {    
  10.           //将本批插入的对象立即写入数据库并释放内存    
  11.           session.flush();    
  12.           session.clear();    
  13.     }    
  14. }    
  15. tx.commit();    
  16. session.close();  
Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction();   
for ( int i=0; i<100000; i++ ) { 
    Customer customer = new Customer(.....); 
    //如果你的 hibernate.cache.use_second_level_cache 是 true, 请在会话级别上关闭他    
    //向(任何一级)缓存中加载大量数据通常也意味着它们很快会被清除出去,这会增加GC开销。  
    session.setCacheMode(CacheMode.IGNORE);
    session.save(customer); 
    if ( i % 50 == 0 ) { 
          //将本批插入的对象立即写入数据库并释放内存 
          session.flush(); 
          session.clear(); 
    } 
} 
tx.commit(); 
session.close();

--------------------------------------------------------------------------------------------------------------

 

JDBC批量操作

 

Statement加批量的方法

Java代码 复制代码  收藏代码
  1.  conn.setAutoCommit(false);      
  2.  Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);      
  3.  for(int x = 0; x < size; x++){      
  4.    stmt.addBatch("INSERT INTO adlogs(ip,website,yyyymmdd,hour,object_id) VALUES('192.168.1.3', 'localhost','20081009',8,'23123')");      
  5.  }      
  6. stmt.executeBatch();      
  7. conn.commit();   
 conn.setAutoCommit(false);   
 Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);   
 for(int x = 0; x < size; x++){   
   stmt.addBatch("INSERT INTO adlogs(ip,website,yyyymmdd,hour,object_id) VALUES('192.168.1.3', 'localhost','20081009',8,'23123')");   
 }   
stmt.executeBatch();   
conn.commit(); 

 

使用PreparedStatement加批量的方法

Java代码 复制代码  收藏代码
  1. try {      
  2.       Class.forName("com.mysql.jdbc.Driver");      
  3.       conn = DriverManager.getConnection(o_url, userName, password);      
  4.       conn.setAutoCommit(false);      
  5.       String sql = "INSERT adlogs(ip,website,yyyymmdd,hour,object_id) VALUES(?,?,?,?,?)";      
  6.       PreparedStatement prest = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);      
  7.       for(int x = 0; x < size; x++){      
  8.          prest.setString(1"192.168.1.1");      
  9.          prest.setString(2"localhost");      
  10.          prest.setString(3"20081009");      
  11.          prest.setInt(48);      
  12.          prest.setString(5"11111111");      
  13.          prest.addBatch();      
  14.       }      
  15.       prest.executeBatch();      
  16.       conn.commit();      
  17.       conn.close();      
  18. catch (SQLException ex) {      
  19.    Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);      
  20. catch (ClassNotFoundException ex) {      
  21.      Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);      
  22. }    
try {   
      Class.forName("com.mysql.jdbc.Driver");   
      conn = DriverManager.getConnection(o_url, userName, password);   
      conn.setAutoCommit(false);   
      String sql = "INSERT adlogs(ip,website,yyyymmdd,hour,object_id) VALUES(?,?,?,?,?)";   
      PreparedStatement prest = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);   
      for(int x = 0; x < size; x++){   
         prest.setString(1, "192.168.1.1");   
         prest.setString(2, "localhost");   
         prest.setString(3, "20081009");   
         prest.setInt(4, 8);   
         prest.setString(5, "11111111");   
         prest.addBatch();   
      }   
      prest.executeBatch();   
      conn.commit();   
      conn.close();   
} catch (SQLException ex) {   
   Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);   
} catch (ClassNotFoundException ex) {   
     Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);   
}  

 

 

注意上面JDBC驱动使用的是mysql的, 好像不支持批量更新,所以大家一定要使用oracle的JDBC驱动试验啊。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值