SpringBoot JPA批量插入

笔者最近一个在搞的一个单机秒杀系统里边有个异步写库的功能:@Scheduled定时任务从内存队列中拿到记录列表,写入数据库,然后清空队列。这样就对写入数据库的性能有一定的要求,所以想起来之前jdbcTemplate是有batchInsert功能的。而springboot jpa也有类似的功能。
实体类和Repository

/**
 *  测试网点表
 * */
@Entity
@Table(name="t_test_outlet")
@Data
public class TOutlet {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "outlet_id")
    private Long outletId;

    @Column(name = "outlet_name", columnDefinition="varchar(50) COMMENT '网点名称' ")
    private String outletName;
    
    @Column(name = "address", columnDefinition="varchar(150) COMMENT '网点地址' ")
    private String address;
}

public interface TOutletRepository extends CrudRepository<TOutlet, Long>, JpaSpecificationExecutor<TOutlet>{
    List<TOutlet> findAll();
}

三种insert方式的对比(插入1000条记录):

@Slf4j
@Service
public class TestDBInsertService {
    
    @Autowired
    private TOutletRepository tOutletRepository;
    
    @PersistenceContext
    private EntityManager entityManager;
    
    //循环save
    public void write10KToDB() {
        long startTime = System.nanoTime();
         
        for(int i=1; i<=1000; i++) {
            TOutlet t = new TOutlet();
            t.setOutletName("网点" + i);
            t.setAddress("地址" + i);
            tOutletRepository.save(t);
        }
        
        long estimatedTime = System.nanoTime() - startTime;
        
        log.info("循环save耗时{} ms" , estimatedTime/1000000);
    }
    
    //saveAll
    public void write10KToDB_batch() {
        long startTime = System.nanoTime();
        
        List<TOutlet> list = new ArrayList<>();
        for(int i=1; i<=1000; i++) {
            TOutlet t = new TOutlet();
            t.setOutletName("网点batch" + i);
            t.setAddress("地址batch" + i);
            list.add(t);
        }
        tOutletRepository.saveAll(list);
        
        long estimatedTime = System.nanoTime() - startTime;
        
        log.info("saveAll耗时{} ms" , estimatedTime/1000000);
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void batchInsert() {
        long startTime = System.nanoTime();
        
        for(int i=1; i<=1000; i++) {
            TOutlet t = new TOutlet();
            t.setOutletName("网点entityManager" + i);
            t.setAddress("地址entityManager" + i);
            entityManager.persist(t);
        }
        entityManager.flush();
        entityManager.clear();
        
        long estimatedTime = System.nanoTime() - startTime;
        
        log.info("entityManager.persist耗时{} ms" , estimatedTime/1000000);
    }
}

输出结果:

[2021-10-05 15:04:03] [ INFO ] [http-nio-8080-exec-4] - 循环save耗时49285 ms
[2021-10-05 15:04:15] [ INFO ] [http-nio-8080-exec-4] - saveAll耗时12377 ms
[2021-10-05 15:04:27] [ INFO ] [http-nio-8080-exec-4] - entityManager.persist耗时12226 ms

经过多次验证,耗时跟上面都比较接近。
从上面结果来看,循环Repository.save(entity)性能最差,saveAll(entities)和直接用entityManager循环执行persist(t)方法性能相当,耗时都只有循环save的25%左右。
entityManager循环执行persist(t)相比saveAll()少了每次去select再决定insert还是update这样一个查询和判断,虽然从上面结果来看,这个提升不是很明显,可能是因为我测试的表本身数据量不大、与数据库之间的带宽1Mbit/s、数据库服务器配置很低只有1核2G内存等原因。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容