使用JDBC批量保存数据(JdbcDaoSupport,JdbcTemplete)

最近做的一个项目中用到了Hibernate的,然后数据库批量插入数据的时候就使用到了hibernate的批处理,但是效率比较低,看网上说还有一些限制,要禁止二级缓存,还要多一个batch_size的配置什么的,不知道是用的不对还是怎么滴,插入一万条数据最快的时候也需要三十多秒时间,慢的五十多秒,比较纠结,然后改用了jdbc的批处理,这里有三张表,Device,Alarm和SyslogAlarm,不过device表可以忽略,用处不大,就是和Alarm有个一对多的关系,Alarm和SyslogAlarm的主键都是手动控制的,不自增,Alarm和SyslogAlarm是一对一的关系,现在控制Alarm和SyslogAlarm的主键值相同,即有关联关系的Alarm和SyslogAlarm的主键值相同,知道其中一个的主键就可以查出另外一张表,SyslogAlarm中有Alarm的外键,因此在这里也不能够让主键自增。

首先需要一个工具类,该工具类的作用是获取同一个Connection链接

//获取同一个connection,参照了hibernate的getCurrentSession的实现

import java.sql.Connection;

import java.sql.SQLException;

import javax.sql.DataSource;



public class ConnectionUtil{



public static final ThreadLocal<Connection> connections = new ThreadLocal<Connection>();

public static Connection getConnection(DataSource dataSource) throws SQLException{

  Connection c = connections.get();

  if(null==c){

    c=dataSource.getConnection();

    connections.set(c);
   }
  return c;   } }

spring的配置文件就不搞了下面是dao层的具体实现,servce层省略

@Repository

@Scope("prototype")

public class SyslogAlarmDao2 extends JdbcDaoSupport implements InitializingBean{

  @resource

  public void setDatasource(DataSource dataSource){

    this.setDataSource(dataSource);

  }

  private long count = 0L;

  String sql1 = "";

  String sql2 = "";

  PreparedStatement ps1 = null;

  PreparedStatement ps2 = null;

  Connection c = null;

   /**
  这里有篇文章可以帮助理解下面这个注解 http://blog.csdn.net/yaerfeng/article/details/8447530
  */

  @PostConstruct
//   public void set() throws SQLException{     sql1 = "sql";//省略,带问号的sql字符串,注意字符串内容不能有;,比如"insert into alarm values(?,?,?,?,?,?);"就是错误的,我因为这个问题纠结了不少时间,"insert into alarm values(?,?,?,?,?,?)"     sql2 ="sql"; //同上     c = ConnectionUtil.getConnection(this.getDataSource());     ps1 = c.prepareStatement(sql1);     ps2 = c.prepareStatement(sql2);
   /**
由于主键是手动控制的,所以需要查一下数据库中已经存在的id的最大值,然后从最大值+1处开始添加数据
alarm和syslogalarm的主键值相同,所以查一个就可以
   */     PreparedStatement ps3
= c.prepareStatement("select max(id) from alarm2");     ResultSet rs = ps3.executeQuery();     while(rs.next()){      count = rs.getLong(1);     }   } public void executeBatch(SyslogAlarm sAlarm) throws SQLException{ //这个可以提取到service层  
if(sAlarm==null||sAlarm.getAlarm ==null){     System.out.println("input error");     return;   }
count++;   ps1.setLong(
1,count); //从sAlarm中取值填充到ps1的sql字符串中  
//..............   ps2.setLong(1,count);   //从sAlarm中取值填充到ps2的sql字符串中  
//..........   ps1.addBatch();   ps2.addBatch();   System.out.println("调用了 "+count+"");   if(count%1000==0){ //为10的时候插入一万条需要4.061秒,为100插入一万数据需要1.403秒,为1000的时候插入一万条数据需要大概需要0.747秒时间     ps1.executeBatch();     ps2.executeBatch();     ps1.clearBatch();     ps2.clearBatch();   }   //ps1.executeBatch(); //执行剩余sql,但是在这里写的话效率较低,因为每次都会调用,实际情况是只需要最后调用一次即可,所以可以单独写一个方法,在测试中的数据满足批处理的全部执行完成之后再调用这个方法
  //比如,现在批处理是1000条一次,如果处理9999条数据,则剩余999条不满1000,则不会插入到数据库中,这个时候可以在执行完前9000条数据之后再执行这两句,对此写了个方法,executeRemainderSQL   
//ps2.executeBatch(); //同上 }
public void executeReminderSQL() throws SQLException{
ps1.executeBatch();
 
ps1.clearBatch();
 
ps2.executeBatch();
  
ps2.clearBatch();
} }

测试

//Service shenglue



@Test

public void testBatchInsert(){

ApplicationContext ac = new ClassPathXmlApplicationContext("spring xml path");

double start = System.currentTimeMillis();

SyslogAlarmService sas = (SyslogAlarmService) ac.getBean("syslogAlarmService");



for(int i=1;i<10001;i++){//当为1000批处理时,插入一万数据需要一秒左右,十万数据7秒左右,20万数据13秒左右,50万数据31秒左右

Device d = new Device();

d.setId(1);



Alarm alarm = new Alarm();

alarm.setDevice(d);

//alarm.set



SyslogAlarm sAlarm = new SyslogAlarm();

sAlarm.setAlarm(alarm);

//sAlarm.set



sas.batchInsert(sAlarm);

}
sas.executeReminderSQL();//执行剩余的sql
double end = System.currentTimeMillis(); System.out.println("执行了 :"+(end-start)/1000+ ""); } }

 

你可能感兴趣的:(jdbc)