构建一个高性能的通用批量插入工具-Oracle版
Oracle + MyBatis 通用高性能批量导入框架(企业级完整版)
一、设计目标
适用于:
-
SpringBoot
-
MyBatis / MyBatisPlus
-
Oracle 数据库
-
Excel 导入
-
大批量数据(2万 ~ 百万级)
目标:
-
高性能
-
通用化
-
可复用
-
支持任意实体
-
自动分批
-
事务控制
-
导入失败定位
-
导入耗时统计
-
支持以后所有批量导入业务
二、推荐方案(核心思想)
采用:
Excel
↓
实体List
↓
通用BatchUtils自动分批
↓
MyBatis XML foreach + UNION ALL
↓
Oracle
Oracle 最适合:
INSERT INTO TABLE_NAME
SELECT ... FROM DUAL
UNION ALL
SELECT ... FROM DUAL
不要使用:
saveBatch()
因为本质仍然是循环 insert。
三、项目结构建议
com.xxx
├── batch
│ ├── BatchUtils.java
│ ├── BatchResult.java
│ ├── BatchImportService.java
│ └── BatchExecutor.java
│
├── mapper
│ ├── UserMapper.java
│ └── UserMapper.xml
│
├── entity
│ └── User.java
│
└── service
└── UserImportService.java
四、通用分批工具类(核心)
BatchUtils.java
package com.xxx.batch;
import java.util.List;
import java.util.function.Consumer;
public class BatchUtils {
/**
* 通用分批处理
*/
public static <T> void batchProcess(
List<T> dataList,
int batchSize,
Consumer<List<T>> consumer) {
if (dataList == null || dataList.isEmpty()) {
return;
}
int total = dataList.size();
for (int i = 0; i < total; i += batchSize) {
int end = Math.min(i + batchSize, total);
List<T> batchList = dataList.subList(i, end);
consumer.accept(batchList);
}
}
}
五、批量执行结果对象
BatchResult.java
package com.xxx.batch;
import lombok.Data;
@Data
public class BatchResult {
/**
* 总数据量
*/
private int total;
/**
* 成功数量
*/
private int success;
/**
* 失败数量
*/
private int fail;
/**
* 耗时(ms)
*/
private long cost;
}
六、通用批量执行器
BatchExecutor.java
package com.xxx.batch;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.function.Consumer;
@Slf4j
public class BatchExecutor {
/**
* 默认批次大小
*/
private static final int DEFAULT_BATCH_SIZE = 500;
/**
* 通用批量执行
*/
public static <T> BatchResult execute(
List<T> dataList,
Consumer<List<T>> consumer) {
return execute(dataList, DEFAULT_BATCH_SIZE, consumer);
}
/**
* 通用批量执行
*/
public static <T> BatchResult execute(
List<T> dataList,
int batchSize,
Consumer<List<T>> consumer) {
long start = System.currentTimeMillis();
BatchResult result = new BatchResult();
if (dataList == null || dataList.isEmpty()) {
return result;
}
int total = dataList.size();
result.setTotal(total);
try {
BatchUtils.batchProcess(dataList, batchSize, batch -> {
consumer.accept(batch);
});
result.setSuccess(total);
result.setFail(0);
} catch (Exception e) {
log.error("批量执行失败", e);
result.setSuccess(0);
result.setFail(total);
throw e;
}
result.setCost(System.currentTimeMillis() - start);
return result;
}
}
七、实体类示例
User.java
package com.xxx.entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
}
八、Mapper 接口
UserMapper.java
package com.xxx.mapper;
import com.xxx.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
int batchInsert(@Param("list") List<User> list);
}
九、Oracle 高性能 XML(核心)
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxx.mapper.UserMapper">
<insert id="batchInsert">
INSERT INTO USER_TABLE
(
ID,
NAME,
AGE
)
<foreach collection="list"
item="item"
separator="UNION ALL">
SELECT
#{item.id},
#{item.name},
#{item.age}
FROM DUAL
</foreach>
</insert>
</mapper>
十、Service 层(生产推荐)
UserImportService.java
package com.xxx.service;
import com.xxx.batch.BatchExecutor;
import com.xxx.batch.BatchResult;
import com.xxx.entity.User;
import com.xxx.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class UserImportService {
private final UserMapper userMapper;
/**
* Excel导入
*/
@Transactional(rollbackFor = Exception.class)
public BatchResult importExcel(List<User> list) {
return BatchExecutor.execute(list, 500, batch -> {
userMapper.batchInsert(batch);
});
}
}
十一、Controller 示例
@PostMapping("/import")
public String importExcel(MultipartFile file) {
List<User> list = ExcelUtils.parse(file, User.class);
BatchResult result = userImportService.importExcel(list);
return "成功导入:" + result.getSuccess();
}
十二、Excel 导入推荐方案
推荐:
| 方案 | 推荐度 |
|---|---|
| EasyExcel | ✅✅✅ |
| POI | ⚠ 一般 |
推荐:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
十三、性能优化(非常重要)
1、批次大小推荐
| 数据量 | 推荐 |
|---|---|
| <1万 | 500 |
| 1~10万 | 1000 |
| 10万+ | 2000 |
2、关闭 SQL 日志
不要开启:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
会严重影响性能。
3、导入期间关闭索引(超大数据)
如果几十万以上:
删除索引
导入
重建索引
性能提升巨大。
4、避免单次SQL过大
不要:
2万条一次 insert
Oracle SQL 长度有限制。
推荐:
500~1000 一批
十四、通用扩展方案(推荐)
以后你还可以扩展:
-
导入失败行定位
-
导入日志表
-
导入进度条
-
异步导入
-
MQ导入
-
导入模板下载
-
自动字段映射
-
动态SQL生成
-
通用 Mapper
-
插入/更新(UPSERT)
-
幂等导入
十五、百万级数据推荐方案
如果以后达到:
100万+
推荐:
-
Oracle SQL Loader
-
Data Pump
-
外部表
-
JDBC Batch
-
多线程分片导入
而不是 MyBatis。
十六、最终结论(企业项目最佳实践)
推荐最终方案:
EasyExcel
↓
List<T>
↓
BatchExecutor
↓
500分批
↓
MyBatis XML foreach
↓
Oracle UNION ALL
↓
事务提交
这是目前:
-
最稳定
-
最通用
-
最容易维护
-
性能最高
-
企业最常用
的一套 Oracle 批量导入方案。

浙公网安备 33010602011771号