最近做的一个项目中用到了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(); } } public void executeBatch(SyslogAlarm sAlarm) throws SQLException{ //这个可以提取到service层 if(sAlarm==null||sAlarm.getAlarm ==null){ System.out.println("input error"); return; } count++; ps1.setLong(,count); //从sAlarm中取值填充到ps1的sql字符串中 //.............. ps2.setLong(,count); //从sAlarm中取值填充到ps2的sql字符串中 //.......... ps1.addBatch(); ps2.addBatch(); System.out.println("调用了 "+count+" 次"); ==){ //为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"); ;i<;i++){//当为1000批处理时,插入一万数据需要一秒左右,十万数据7秒左右,20万数据13秒左右,50万数据31秒左右 Device d = new Device(); d.setId(); 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.+ " 秒"); } }