MybatisPlus實(shí)現(xiàn)真正批量插入的詳細(xì)步驟
在實(shí)際開發(fā)中,批量插入是提高數(shù)據(jù)處理效率的常用手段。MyBatis-Plus 作為 MyBatis 的增強(qiáng)工具,提供了多種方式來實(shí)現(xiàn)批量插入。然而,默認(rèn)的
saveBatch
方法在底層實(shí)際上是逐條插入,性能上并不理想。本文將詳細(xì)介紹如何通過 MyBatis-Plus 實(shí)現(xiàn)真正高效的批量插入,包括手動拼接 SQL、使用IService
接口以及自定義insertBatchSomeColumn
方法,并提供性能優(yōu)化建議。
一、通過 XML 手動拼接 SQL 實(shí)現(xiàn)批量插入
優(yōu)勢
- 高效:一次性執(zhí)行批量插入,減少數(shù)據(jù)庫交互次數(shù)。
- 靈活:可以根據(jù)需要選擇性插入部分字段,減少不必要的數(shù)據(jù)傳輸。
缺點(diǎn)
- 維護(hù)成本高:每個表都需要手動編寫對應(yīng)的 XML SQL。
- 不支持自動生成主鍵:如果表中有自增主鍵,需額外處理。
實(shí)現(xiàn)步驟
編寫 Mapper XML 文件
以 history_summary
表為例,編寫批量插入的 XML:
<insert id="insertBatch" parameterType="java.util.List"> INSERT INTO history_summary (key_id, business_no, status, customer_id, instruction_id, customer_business_no, dept_id, doc_type_id, doc_page_no, document_version, result_mongo_doc_id, is_active, problem_status, tran_source, document_count_page, is_rush, is_deleted, document_tran_time, customer_tran_time, preprocess_recv_time, delete_time, create_time, complete_time, version, zip_name, document_no, new_task_type, handle_begin_time, handle_end_time, cost_seconds, char_num, ocr_result_mongo_id, reserve_text2, reserve_text3, reserve_text1, reserve_text4, reserve_text5, ocr_result_type, repeat_status, form_version, repetition_count) VALUES <foreach collection="list" item="item" separator=","> (#{item.keyId}, #{item.businessNo}, #{item.status}, #{item.customerId}, #{item.instructionId}, #{item.customerBusinessNo}, #{item.deptId}, #{item.docTypeId}, #{item.docPageNo}, #{item.documentVersion}, #{item.resultMongoDocId}, #{item.isActive}, #{item.problemStatus}, #{item.tranSource}, #{item.documentCountPage}, #{item.isRush}, #{item.isDeleted}, #{item.documentTranTime}, #{item.customerTranTime}, #{item.preprocessRecvTime}, #{item.deleteTime}, #{item.createTime}, #{item.completeTime}, #{item.version}, #{item.zipName}, #{item.documentNo}, #{item.newTaskType}, #{item.handleBeginTime}, #{item.handleEndTime}, #{item.costSeconds}, #{item.charNum}, #{item.ocrResultMongoId}, #{item.reserveText2}, #{item.reserveText3}, #{item.reserveText1}, #{item.reserveText4}, #{item.reserveText5}, #{item.ocrResultType}, #{item.repeatStatus}, #{item.formVersion}, #{item.repetitionCount}) </foreach> </insert>
在 Mapper 接口中定義批量插入方法
public interface historySummaryMapper extends BaseMapper<historySummary> { @Insert({ "<script>", "INSERT INTO history_summary ", "(key_id, business_no, status, customer_id, instruction_id, customer_business_no, dept_id, doc_type_id, doc_page_no, document_version, result_mongo_doc_id, is_active, problem_status, tran_source, document_count_page, is_rush, is_deleted, document_tran_time, customer_tran_time, preprocess_recv_time, delete_time, create_time, complete_time, version, zip_name, document_no, new_task_type, handle_begin_time, handle_end_time, cost_seconds, char_num, ocr_result_mongo_id, reserve_text2, reserve_text3, reserve_text1, reserve_text4, reserve_text5, ocr_result_type, repeat_status, form_version, repetition_count)", "VALUES ", "<foreach collection='list' item='item' index='index' separator=','>", "(#{item.keyId}, #{item.businessNo}, #{item.status}, #{item.customerId}, #{item.instructionId}, #{item.customerBusinessNo}, #{item.deptId}, #{item.docTypeId}, #{item.docPageNo}, #{item.documentVersion}, #{item.resultMongoDocId}, #{item.isActive}, #{item.problemStatus}, #{item.tranSource}, #{item.documentCountPage}, #{item.isRush}, #{item.isDeleted}, #{item.documentTranTime}, #{item.customerTranTime}, #{item.preprocessRecvTime}, #{item.deleteTime}, #{item.createTime}, #{item.completeTime}, #{item.version}, #{item.zipName}, #{item.documentNo}, #{item.newTaskType}, #{item.handleBeginTime}, #{item.handleEndTime}, #{item.costSeconds}, #{item.charNum}, #{item.ocrResultMongoId}, #{item.reserveText2}, #{item.reserveText3}, #{item.reserveText1}, #{item.reserveText4}, #{item.reserveText5}, #{item.ocrResultType}, #{item.repeatStatus}, #{item.formVersion}, #{item.repetitionCount})", "</foreach>", "</script>" }) int insertBatch(@Param("list") List<historySummary> list); }
在 Service 層調(diào)用批量插入方法
@Service public class historySummaryServiceImpl extends ServiceImpl<historySummaryMapper, historySummary> implements IhistorySummaryService { @Autowired private historySummaryMapper mapper; @Transactional(rollbackFor = Exception.class) public boolean batchInsert(List<historySummary> list) { int result = mapper.insertBatch(list); return result > 0; } }
使用示例
@RestController @RequestMapping("/api/business") public class BusinessController { @Autowired private IhistorySummaryService service; @PostMapping("/batchInsert") public ResponseEntity<String> batchInsert(@RequestBody List<historySummary> list) { boolean success = service.batchInsert(list); if (success) { return ResponseEntity.ok("批量插入成功"); } else { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("批量插入失敗"); } } }
看到這里應(yīng)該頭皮發(fā)麻了吧。這也是我為什么想分享這篇文章的原因!?。?/p>
涉及到多字段批量插入,還是推薦下面的方法。
二、使用 MyBatis-Plus IService 接口的 saveBatch 方法
MyBatis-Plus 提供的 saveBatch
方法簡化了批量插入的操作,但其底層實(shí)際上是逐條插入,因此在處理大量數(shù)據(jù)時性能不佳。
優(yōu)勢
- 簡便易用:無需手動編寫 SQL,直接調(diào)用接口方法即可。
- 自動事務(wù)管理:內(nèi)置事務(wù)支持,確保數(shù)據(jù)一致性。
缺點(diǎn)
- 性能有限:底層逐條插入,面對大數(shù)據(jù)量時效率較低。
- 批量大小受限:默認(rèn)批量大小可能不適用于所有場景,需要手動調(diào)整。
提升性能的方法
配置數(shù)據(jù)庫連接參數(shù)
在數(shù)據(jù)庫的連接 URL 中添加 rewriteBatchedStatements=true
,啟用批量重寫功能,提升批量插入性能。
spring.datasource.url=jdbc:mysql://localhost:3306/your_database?rewriteBatchedStatements=true
調(diào)整批量大小
在調(diào)用 saveBatch
方法時,合理設(shè)置批量大?。ㄈ?1000 條一批),以平衡性能和資源消耗。
@Transactional(rollbackFor = {Exception.class}) public boolean saveBatch(Collection<T> entityList, int batchSize) { String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE); return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> { sqlSession.insert(sqlStatement, entity); }); }
三、insertBatchSomeColumn 方法實(shí)現(xiàn)批量插入
為了實(shí)現(xiàn)真正高效的批量插入,可以使用 insertBatchSomeColumn
方法,實(shí)現(xiàn)一次性批量插入。
優(yōu)勢
- 高效:一次性執(zhí)行批量插入,減少數(shù)據(jù)庫交互次數(shù)。
- 靈活:可選擇性插入部分字段,優(yōu)化數(shù)據(jù)傳輸。(通過實(shí)體類屬性)
實(shí)現(xiàn)步驟
1. 自定義SQL注入器實(shí)現(xiàn)DefaultSqlInjector,添加InsertBatchSomeColumn方法
public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) { List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo); methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE)); return methodList; } }
2. 將MySqlInjector注入到Bean中
@Configuration public class MyBatisConfig { @Bean public MySqlInjector sqlInjector() { return new MySqlInjector(); } @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //添加分頁插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //添加樂觀鎖插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }
3. 繼承Mybatis-plus的BaseMapper,添加插入方法
public interface MyBaseMapper<T> extends BaseMapper<T> { int insertBatchSomeColumn(Collection<T> entityList); }
@Mapper public interface WorkingBusinessHistoryMapper extends MyBaseMapper<BusinessHistory> { }
注意事項(xiàng)
- 批量大小:根據(jù)數(shù)據(jù)庫和應(yīng)用的性能,合理設(shè)置批量插入的大小,避免單次插入過多數(shù)據(jù)導(dǎo)致內(nèi)存溢出或數(shù)據(jù)庫壓力過大。
- 事務(wù)管理:確保批量操作在事務(wù)中執(zhí)行,以保證數(shù)據(jù)的一致性和完整性。
- 錯誤處理:在批量操作中,如果某條記錄插入失敗,需要有相應(yīng)的機(jī)制進(jìn)行回滾或記錄失敗信息。
四、性能優(yōu)化建議
為了進(jìn)一步提升批量插入的性能,可以采取以下優(yōu)化措施:
1. 開啟批量重寫功能
在數(shù)據(jù)庫的連接 URL 中添加 rewriteBatchedStatements=true
,以優(yōu)化批量插入的性能。
spring.datasource.url=jdbc:mysql://localhost:3306/your_database?rewriteBatchedStatements=true
2. 合理設(shè)置批量大小
根據(jù)具體業(yè)務(wù)場景和數(shù)據(jù)庫性能,調(diào)整批量大?。ㄈ?1000 條一批),避免單次插入過多數(shù)據(jù)。
int batchSize = 1000; for (int i = 0; i < list.size(); i += batchSize) { List<historySummary> batchList = list.subList(i, Math.min(i + batchSize, list.size())); mapper.insertBatchSomeColumn(batchList); }
3. 使用事務(wù)管理
確保批量操作在事務(wù)中執(zhí)行,避免部分插入成功導(dǎo)致數(shù)據(jù)不一致。
@Transactional(rollbackFor = Exception.class) public boolean batchInsert(List<historySummary> list) { int result = mapper.insertBatchSomeColumn(list); return result > 0; }
4. 索引優(yōu)化
對插入頻繁的表,合理設(shè)計(jì)索引,避免過多不必要的索引影響插入性能。盡量減少在批量插入時的索引數(shù)量,插入完成后再創(chuàng)建必要的索引。
5. 禁用自動提交
在批量插入過程中,禁用自動提交,減少事務(wù)提交的次數(shù),提高性能。
jdbcTemplate.execute((ConnectionCallback<Void>) connection -> { connection.setAutoCommit(false); // 執(zhí)行批量插入操作 connection.commit(); return null; });
參考
MybatisPlus自定義insertBatchSomeColumn實(shí)現(xiàn)真正批量插入 - CSDN博客
MybatisPlus如何實(shí)現(xiàn)insertBatchSomeColumn進(jìn)行批量增加 - 億速云
MySQL Connector/J Documentation
到此這篇關(guān)于MybatisPlus實(shí)現(xiàn)真正批量插入的文章就介紹到這了,更多相關(guān)MybatisPlus批量插入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用JavaMail API發(fā)送和接收郵件的代碼示例
JavaMail是Oracle甲骨文開發(fā)的Java郵件類API,支持多種郵件協(xié)議,這里我們就來看一下Java使用JavaMail API發(fā)送和接收郵件的代碼示例2016-06-06關(guān)于springboot2整合lettuce啟動卡住問題的解決方法
Lettuce和Jedis的都是連接Redis Server的客戶端程序,下面這篇文章主要給大家介紹了關(guān)于springboot2整合lettuce啟動卡住問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-12-12使用Spring實(shí)現(xiàn)@Value注入靜態(tài)字段
這篇文章主要介紹了使用Spring實(shí)現(xiàn)@Value注入靜態(tài)字段方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05Java?FTP協(xié)議實(shí)現(xiàn)文件下載功能
FTP(File?Transfer?Protocol)就是文件傳輸協(xié)議。通過FTP客戶端從遠(yuǎn)程FTP服務(wù)器上拷貝文件到本地計(jì)算機(jī)稱為下載,將本地計(jì)算機(jī)上的文件復(fù)制到遠(yuǎn)程FTP服務(wù)器上稱為上傳,上傳和下載是FTP最常用的兩個功能2022-11-11一文詳解Spring中的HttpMessageNotReadableException異常處理
這篇文章主要為大家詳細(xì)介紹了Spring中的HttpMessageNotReadableException異常,分析其產(chǎn)生的原因并通過實(shí)際代碼示例展示如何有效地捕獲和處理這一異常,感興趣的可以了解下2025-02-02