Spring Data JPA - 批量插入实例

本文介绍了如何通过JPA批量操作和多线程技术优化SpringDataJPA的批量插入性能。通过配置JPA批量操作、重写save方法以及引入多线程,可以显著提高数据插入效率。实验数据显示,使用多线程插入1W和2W数据分别比单纯开启JPA批量操作快约50%。

之前写过两篇博文讲解了之前项目中如何使用JPA批量插入:

Spring Data JPA批量插入过慢及其优化 —— 自定义Repository_Lazyafei的博客-优快云博客_jpa批量插入优化

Spring Data JPA批量插入过慢及其优化 —— 泛型提炼公用batchSave方法、引入多线程_Lazyafei的博客-优快云博客

今天举个例子,并把源码放出来,项目使用国产神通数据库,其他数据库请自行修改。

GitHub: lazyafei/spring-data-jpa-practice Gitee地址:lazyafei/spring-data-jpa-practice

由于JPA自身机制(详情移步:Spring - Saving Entities),批量插入本质是单条单条进行插入,从这种机制而言就难以快速插入数据。并且在插入单条数据的同时还会调用isNew方法判断是否是新增数据,导致插入数据稍多一些就明显感觉慢。

在不拼接sql批量插入的情况下有以下优化措施:

1、开启JPA批量操作(需要保证实体id不是自增主键GenerationType.IDENTITY)

spring:
  #配置JPA
  jpa:
    database: ORACLE
    hibernate:
      naming_strategy: org.hibernate.cfg.DefaultNamingStrategy
    properties:
      hibernate:
        format_sql: false
        jdbc:
          batch_size: 1000
          batch_versioned_data: true
        order_inserts: true
        order_updates: true
        show_sql: true

2、重写save方法,避免isNew判断

3、多线程处理,性能瓶颈在于数据库 (需要注意BatchSaveRepository.java中batch_size与max_thread大小的设置,一般来说batch_size设置为1000左右,max_thread设置为cpu核心数*2);

//每个线程分的数据量
private final Integer BATCH_SIZE = 1500;
//最大线程数(建议最大电脑核心数*2)
private final Integer MAX_THREAD = 4;

对比图(效率基本提升1倍): 

1W数据:

开启JPA批量:(9.4s)

引入多线程:(5s)

2W数据:

开启JPA批量:(19s)

引入多线程:(10s) 

批量插入在不同的技术场景中有不同的实现方法,其应用场景广泛,同时也有一些需要注意的事项。 ### 批量插入的方法 - **JDBC批量插入**:使用`PreparedStatement`加批量的方法。示例代码如下,通过`addBatch()`方法将多个插入语句添加到批量操作中,最后使用`executeBatch()`执行批量插入操作: ```java try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(o_url, userName, password); conn.setAutoCommit(false); String sql = "INSERT adlogs(ip,website,yyyymmdd,hour,object_id) VALUES(?,?,?,?,?)"; PreparedStatement prest = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY); for(int x = 0; x < size; x++){ prest.setString(1, "192.168.1.1"); prest.setString(2, "localhost"); prest.setString(3, "20081009"); prest.setInt(4, 8); prest.setString(5, "11111111"); prest.addBatch(); } prest.executeBatch(); conn.commit(); conn.close(); } catch (SQLException ex) { Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex); } catch (ClassNotFoundException ex) { Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex); } ``` - **MyBatis批量插入**:在项目使用MyBatis时,可以使用MyBatis的`foreach`功能进行批量插入。例如在程序中封装一个`List`集合对象,然后把该集合中的实体插入到数据库中 [^2][^3]。 ### 批量插入的应用场景 - **数据导入**:当需要将大量数据从一个数据源(如文件)导入到数据库时,批量插入可以显著提高导入效率。 - **日志记录**:日志一般先写在文件中,后续需要将这些日志数据批量插入到数据库中进行存储和分析 [^2]。 - **批量上传附件信息**:在处理批量上传附件的需求时,将多个附件的信息批量插入数据库中 [^3]。 ### 批量插入的注意事项 - **批量插入的顺序优化**:合理安排插入顺序可以提高性能和效率。 - **事务处理**:在`serviceImpl`方法中,避免在数据库操作上使用`try-catch`,以防止事务失效。尽管不加事务时批量插入部分失败也会全部回滚,但为了确保事务的正常处理,仍需注意 [^1][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值