写完后才发现,日志里多条insert
和批量插入,时间差不多是什么鬼?这个saveAll()有毒 🙃
问题描述 一组数据,批量插入,使用JPA 的saveAll()
方法,发现还是一条数据一条数据的插入
问题原因 @GeneratedValue Spring Boot JPA Hibernate saveAll 速度慢原因调查与调优
搜了一下,大概原因和上面差不多,基本上在定义model 时,都这样处理id
1 2 3 @Id @GeneratedValue (strategy = GenerationType.IDENTITY)private Long id;
@GeneratedValue(strategy = GenerationType.IDENTITY)
这个注解,会由hibernate主键策略生成器生成自增id,这样就不用自己控制id,所以存入数据时,数据一个一个获取id,导致数据一条一条插入,所以一个批量插入必要条件要自己设置数据id
缺少配置 配置spring.jpa.properties.hibernate.jdbc.batch_size=500
size根据自己需要设置
还建议开启一个配置spring.jpa.properties.hibernate.generate_statistics=true
一个比较坑的点就是,就算你批量插入了数据,如果你开启spring.jpa.show-sql=true
配置,你还是会看到无数条insert 语句
通过generate_statistics
配置你可以看到SQL总的执行情况
1 2 3 4 5 6 7 8 9 10 11 38864299 nanoseconds spent acquiring 1 JDBC connections; 0 nanoseconds spent releasing 0 JDBC connections; 59624299 nanoseconds spent preparing 1 JDBC statements; 110488775 nanoseconds spent executing 1 JDBC statements; 0 nanoseconds spent executing 0 JDBC batches; 0 nanoseconds spent performing 0 L2C puts; 0 nanoseconds spent performing 0 L2C hits; 0 nanoseconds spent performing 0 L2C misses; 0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections); 0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections) }
batchs 那里就是批量插入情况
解决方案
model里面ID不使用@GeneratedValue
注解,在拼装数据时自己手动设置ID
开启spring.jpa.properties.hibernate.jdbc.batch_size
配置
额外问题 实现了批量插入,但是可以看到日志里还是有无数条select
语句,就想安安静静插个数据,咋就这么难呢?
搜了一下,发现了原因
1 2 3 4 5 6 7 8 9 10 11 12 13 @Transactional public <S extends T> List<S> saveAll (Iterable<S> entities) { Assert.notNull(entities, "The given Iterable of entities not be null!" ); List<S> result = new ArrayList(); Iterator var3 = entities.iterator(); while (var3.hasNext()) { S entity = var3.next(); result.add(this .save(entity)); } return result; }
saveAll()
里调用了 save()
方法
1 2 3 4 5 6 7 8 9 @Transactional public <S extends T> S save (S entity) { if (this .entityInformation.isNew(entity)) { this .em.persist(entity); return entity; } else { return this .em.merge(entity); } }
根据网上的说法,isNew()
为真的话,不会先select查询id是否存在,所以只要重写isNew()
方法,就可以避免这种情况
1 2 3 4 5 6 7 8 9 10 @Entity public class MyThing implements Persistable <T > { ... @Override public boolean isNew () { return true ; } }