MybatisPlus實現(xiàn)真正批量插入的詳細步驟
在實際開發(fā)中,批量插入是提高數(shù)據(jù)處理效率的常用手段。MyBatis-Plus 作為 MyBatis 的增強工具,提供了多種方式來實現(xiàn)批量插入。然而,默認的
saveBatch方法在底層實際上是逐條插入,性能上并不理想。本文將詳細介紹如何通過 MyBatis-Plus 實現(xiàn)真正高效的批量插入,包括手動拼接 SQL、使用IService接口以及自定義insertBatchSomeColumn方法,并提供性能優(yōu)化建議。
一、通過 XML 手動拼接 SQL 實現(xiàn)批量插入
優(yōu)勢
- 高效:一次性執(zhí)行批量插入,減少數(shù)據(jù)庫交互次數(shù)。
- 靈活:可以根據(jù)需要選擇性插入部分字段,減少不必要的數(shù)據(jù)傳輸。
缺點
- 維護成本高:每個表都需要手動編寫對應(yīng)的 XML SQL。
- 不支持自動生成主鍵:如果表中有自增主鍵,需額外處理。
實現(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ù)據(jù)時性能不佳。
優(yōu)勢
- 簡便易用:無需手動編寫 SQL,直接調(diào)用接口方法即可。
- 自動事務(wù)管理:內(nèi)置事務(wù)支持,確保數(shù)據(jù)一致性。
缺點
- 性能有限:底層逐條插入,面對大數(shù)據(jù)量時效率較低。
- 批量大小受限:默認批量大小可能不適用于所有場景,需要手動調(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 方法實現(xiàn)批量插入
為了實現(xiàn)真正高效的批量插入,可以使用 insertBatchSomeColumn 方法,實現(xiàn)一次性批量插入。
優(yōu)勢
- 高效:一次性執(zhí)行批量插入,減少數(shù)據(jù)庫交互次數(shù)。
- 靈活:可選擇性插入部分字段,優(yōu)化數(shù)據(jù)傳輸。(通過實體類屬性)
實現(xiàn)步驟
1. 自定義SQL注入器實現(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> {
}注意事項
- 批量大小:根據(jù)數(shù)據(jù)庫和應(yīng)用的性能,合理設(shè)置批量插入的大小,避免單次插入過多數(shù)據(jù)導致內(nèi)存溢出或數(shù)據(jù)庫壓力過大。
- 事務(wù)管理:確保批量操作在事務(wù)中執(zhí)行,以保證數(shù)據(jù)的一致性和完整性。
- 錯誤處理:在批量操作中,如果某條記錄插入失敗,需要有相應(yīng)的機制進行回滾或記錄失敗信息。
四、性能優(yōu)化建議
為了進一步提升批量插入的性能,可以采取以下優(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í)行,避免部分插入成功導致數(shù)據(jù)不一致。
@Transactional(rollbackFor = Exception.class)
public boolean batchInsert(List<historySummary> list) {
int result = mapper.insertBatchSomeColumn(list);
return result > 0;
}4. 索引優(yōu)化
對插入頻繁的表,合理設(shè)計索引,避免過多不必要的索引影響插入性能。盡量減少在批量插入時的索引數(shù)量,插入完成后再創(chuàng)建必要的索引。
5. 禁用自動提交
在批量插入過程中,禁用自動提交,減少事務(wù)提交的次數(shù),提高性能。
jdbcTemplate.execute((ConnectionCallback<Void>) connection -> {
connection.setAutoCommit(false);
// 執(zhí)行批量插入操作
connection.commit();
return null;
});參考
MybatisPlus自定義insertBatchSomeColumn實現(xiàn)真正批量插入 - CSDN博客
MybatisPlus如何實現(xiàn)insertBatchSomeColumn進行批量增加 - 億速云
MySQL Connector/J Documentation
到此這篇關(guān)于MybatisPlus實現(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啟動卡住問題的解決方法,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2021-12-12
使用Spring實現(xiàn)@Value注入靜態(tài)字段
這篇文章主要介紹了使用Spring實現(xiàn)@Value注入靜態(tài)字段方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
一文詳解Spring中的HttpMessageNotReadableException異常處理
這篇文章主要為大家詳細介紹了Spring中的HttpMessageNotReadableException異常,分析其產(chǎn)生的原因并通過實際代碼示例展示如何有效地捕獲和處理這一異常,感興趣的可以了解下2025-02-02

