Jpa和JDBC等批量插入比较

  以前在写一些批量操作时感觉JPA提供的数组操作效率很差,所以就尝试用EntityManager进行批量提交来提高效率,最近有时间并将JdbcTemplate也加入对比了一下它们的执行效率。

  pom.xml

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

  application.properties配置批量大小

1
spring.jpa.properties.hibernate.jdbc.batch_size = 1000

  第一种方法:通过EntityManager,定制批量插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Repository
public class BatchOpsRepositoryImpl<T> implements BatchOpsRepository<T> {
@Qualifier("entityManagerFactorySqlServer")
@Autowired
private EntityManager em;

@Modifying
@Transactional(value = "transactionManagerSqlServer")
public void batchInsert(List<T> list) {
for (int i = 0; i < list.size(); i++) {
em.persist(list.get(i));
if (i % 1000 == 0 || i == (list.size() - 1)) {//1000条执行一次,或不足1000条
System.out.println("1000条执行一次,或不足1000条");
em.flush();
em.clear();
}
}
}

@Modifying
@Transactional(value = "transactionManagerSqlServer")
public void batchUpdate(List<T> list) {
for (int i = 0; i < list.size(); i++) {
em.merge(list.get(i));
if (i % 1000 == 0 || i == (list.size() - 1)) {//1000条执行一次,或不足1000条
System.out.println("1000条执行一次,或不足1000条");
em.flush();
em.clear();
}
}
}
}

  第二种方法,直接使用repository.saveAll();

  第三种方法,使用jdbcTemplate.batchUpdate();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@Autowired
private JdbcTemplate jdbcTemplate;
//JDBC批量存储
private void batchInsertStationInfo(List<StationInfo> list){
jdbcTemplate.batchUpdate("INSERT INTO T_XXXX (DM,JM,MC,CS,GS) VALUES (?,?,?,?,?);", new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
preparedStatement.setString(1,list.get(i).getDm());
preparedStatement.setString(2,list.get(i).getJm());
preparedStatement.setString(3,list.get(i).getMc());
preparedStatement.setString(4,list.get(i).getCs());
preparedStatement.setString(5,list.get(i).getGs());
}

@Override
public int getBatchSize() {
return list.size();
}
});
}

  执行测试,数据量大约5000。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//先清除原数据
stationInfoRepository.deleteAllInBatch();
long startTime=System.currentTimeMillis(); //获取开始时间
batchOpsRepository.batchInsert(infos);
long endTime=System.currentTimeMillis(); //获取结束时间

stationInfoRepository.deleteAllInBatch();
long startTime1=System.currentTimeMillis(); //获取开始时间
stationInfoRepository.saveAll(infos);
long endTime1=System.currentTimeMillis(); //获取结束时间

stationInfoRepository.deleteAllInBatch();
long startTime2=System.currentTimeMillis(); //获取开始时间
batchInsertStationInfo(infos);
long endTime2=System.currentTimeMillis(); //获取结束时间
System.out.println("batchInsert运行时间: "+(endTime-startTime)+"ms");
System.out.println("saveAll运行时间: "+(endTime1-startTime1)+"ms");
System.out.println("jdbc运行时间: "+(endTime2-startTime2)+"ms");

  运行结果如下。

1
2
3
batchInsert运行时间: 3236ms
saveAll运行时间: 32288ms
jdbc运行时间: 2179ms

  调试可以发现,batchInsert每隔1000条提交一次数据库操作,saveAll则需要在新增前做查询服务,并且每次提交一条新增服务,而JDBC则是效率最高的一个。

  batchInsert相较jdbc,效率相差不大,且代码量小,所以选择继续使用batchInsert