[转]ibatis +spring ,hibernate 批量操作心得

 
 
http://www.blogjava.net/zp0127/default.html?page=2
 

程序功能:
使用ibatis+spring将oracle数据库中的tfile表中的数据抽取到db2数据库的tfile表,这两个表的结构相同。

测试环境:
celeron M 1.4/512M/mysql 5.0数据库
代码:

public   static   void  main(String[] args)  {
        
// TODO Auto-generated method stub
        ClassPathResource resource = new ClassPathResource(
                
"applicationContext.xml");
        BeanFactory factory 
= new XmlBeanFactory(resource);
        TFileDAO tFileDao 
= (TFileDAO) factory.getBean("tfile");

        TFileDAO test2FileDao 
= (TFileDAO) factory.getBean("test2tfile");
        
//获取全部数据
        List list = tFileDao.getAll();
        
//开启事务
        DataSourceTransactionManager txm = (DataSourceTransactionManager) factory
                .getBean(
"test2transactionManager");
        DefaultTransactionDefinition def 
= new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status 
= txm.getTransaction(def);
        
try {
            test2FileDao.getSqlMapClient().startBatch();
            
for (int i = 0, count = list.size(); i < count; i++{
            
//插入数据
                test2FileDao.insert((TFile) list.get(i));
            }

            test2FileDao.getSqlMapClient().executeBatch();// 这两句有问题,请见Spring+ibatis心得2!
        }
 catch (Exception e) {
            txm.rollback(status);
            System.out.println(e);
        }

        txm.commit(status);
        System.out.println(list.size());
    }
1、保证使用长事务,不要在每个插入都事务提交,这样性能可以有很大幅度的提升
2、使用 test2FileDao.getSqlMapClient().startBatch();
              test2FileDao.getSqlMapClient().executeBatch();
             可以发起jdbc对批量数据插入的优化与自动代码压缩功能。

      结语:这次使用ibatis在同样的硬件、数据库、数据条数的环境下测试,在不起用batch,所有数据库,数据池特性均使用默认设置情况下使用19秒,并且使用一次性将数据读入内存的方式,效果优于hibernate,所以真信优化后的程序应该比hibernate效率更高。但是从程序编写方面来讲,hibernate省去了过多的代码,可以让程序员更轻松些。
 
1、上回的心得中我强调了startBatch()的批处理的作用,但是其中的使用是个错误用法,并没有发挥出startBatch()的实力,对此给与观众的误导我将在此表示到欠,并贴出正确的用法
public   class  LocalDaoImpl  extends  SqlMapClientDaoSupport  implements  LocalDao  {

    
public void insertBuNaTaxBatLst(final PaginatedList list)
    
{
         getSqlMapClientTemplate().execute(
new SqlMapClientCallback() {
                
public Object doInSqlMapClient(SqlMapExecutor executor)
                        
throws SQLException {
                    executor.startBatch();
                    
// do some iBatis operations here
                    for(int i=0,count=list.size();i<count;i++)
                    
{    
                        executor.insert(
"insertBuNaTaxBatLst", list.get(i));
                        
if (i % 50 == 0{
                            System.out.println(
"----" + i);//没有意义只为测试
                        }

                    }

                    executor.executeBatch();
                    
return null;
                }

            }
);
    }


}

这样才能利用上startBatch()威力。
2、注意ibatis的事物默认情况下是自动提交的,如果发现速度上有问题可以留意一下,ibatis只有在显示的声明事物管理的情况下才自动将事物管理改为不自动方式。
3、还是startBatch(),据我测试分析这个鬼东西只有在executeBatch(),才把所有的语句提交到数据库,在提交之前缓存中保留了大量的sql语句和数据对象,很有可能out of memony,对此要留意,可以在大量数据要做插入时,分批用Batch,如:有40000条数据可将其分为4个Batch块,让后将这4个Batch用一个事物提交以保证数据完整性。
注:最近在做数据抽取项目,愿与大家沟通心得

 

  到一个这东西都写到3了,针对上回说到30000条数据的批量插入工作。30000条数据的批量插入在一个事务里处理固然是快,但是这只是测试环境,30000条数据在数据库的缓存里必然对数数据库的缓存和锁数量都是一个大的挑战,固在新的程序中我们使用了分批事务提交的方式,这样为了保持数据的正确行就只能人为控制数据库中已被插入的数据是否delete掉。另外,使用Batch块提交会引发一个问题就是,如果batch块中发生了异常,我们得不到异常数据的行号即任何信息,所以只能是鱼和熊掌不可兼得(我已关注过insert方法中返回pk的方法了,但好像在batch中他反回不了出错的行号,也许是我没有找到方法,如有人有好方法请共享一下,在这里表示感谢),大家酌情考虑吧,只能到到自己需要的平衡点了。
      建议:如果对数据的准确性毋庸置疑的话就是用batch处理。如果不能确定准确性的话,如果对那条数据出错无所谓的话就也可以用batch,但是非要返回出错行号的话就不要用batch了,直接在外面套用一个事务,然后try catch一下,处理一下行号。

 

========================================

程序功能:
使用hibernate+spring将oracle数据库中的tfile表中的数据抽取到db2数据库的tfile表,这两个表的结构相同。(原本要使用一些Spring的特性,但是程序改来改去发现Spring特性一个都没用上,实际上完全可以由hibernate创建两个sessionFactory完成)
测试环境:
celeron M 1.4/512M/mysql 5.0数据库
代码:

 

public   void  save()  {
  Session session
= fileDAO.getDataSession();
  Session session2
= fileDAO2.getDataSession();
  Transaction tx 
=session2.beginTransaction();
  
int count=0;
  List list 
=fileDAO.getList(session, count);
  
while(list.size()!=0)
  
{
   
for(int i=0,num =list.size();i<num;i++)
   
{
    session2.save(list.get(i));
    session.evict(list.get(i));
    
if(num%50==0)
    
{
     session2.flush();
     session2.clear();
    }

   }

   count
= count+500;
   list 
=fileDAO.getList(session, count);
   
  
// System.out.println(count);
   
  }

  tx.commit();
  session.close();
  session2.close();
}

 

配置文件:

 

< prop  key ="hibernate.jdbc.batch_size" > 50 </ prop >
< prop  key ="hibernate.cache.use_second_level_cache" > false </ prop >

 

 

1、为保证不会出现内存溢出
   hibernate.jdbc.batch_size 设为 20-50
   并在代码中没隔50个insert后手工清理数据 
     

 

  if (num % 50 == 0 )
    
{
     session2.flush();
     session2.clear();
    }

 

2、为保证减少二级缓存导致的过多的内存开销关,闭二级缓存
  hibernate.cache.use_second_level_cache 设为false
3、保证使用长事务,不要在每个插入都事务提交,这样性能可以有很大幅度的提升(由于原先配的事务没有正常运行,在初次测试时此程序插入4万条数据用了12分钟,使用了长事务后仅为34秒)
4、使用ScrollableResults(提供了数据库的游标特性)然后插入的效果好像要优于分批抓取分批插入的效果,(4万条数据,用ScrollableResult耗时29秒)但网上有人声称批量抓取插入的效果要好可能在远程数据库的情况下批量抓取的可靠性更高一点的原因。这一点我询问过公司里做数据库比较好的人,批量处理数据是要使用游标处理的。就游标而言分为动态游标,静态游标,动态游标慢于静态游标,静态游标慢于静态查询,但是如果用分批抓取数据的话就涉及到数据分段截取,为保证每次分段截取时数据的正确性,应该要预先对数据处理,所以批量抽取数据的速度可能会慢一些。以下为使用ScrollableResult的程序
  

 

Session session =  fileDAO.getDataSession();
  Session session2
=  fileDAO2.getDataSession();
  Transaction tx 
= session2.beginTransaction();
  ScrollableResults tFiles 
=  session.createQuery(
    
" from TFile as model  " ).setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY);
  
int  count = 0 ;
  
while (tFiles.next())
  
{
   session2.save(tFiles.get(
0));
   
if(++count%50==0)
    
{
     session2.flush();
     session2.clear();
    }

  }

  tx.commit();
  session.close();

你可能感兴趣的:(spring,数据结构,oracle,Hibernate,ibatis)