在 MyBatis 中,批量操作是处理高并发数据写入的核心场景之一。通过 SqlSessionFactory 配置批处理执行器(ExecutorType.BATCH),可以显著提升数据库操作的效率。本文将结合 Spring 框架,深入解析如何高效配置和使用 MyBatis 的批量执行功能,并提供性能优化策略。
在 pom.xml 中引入 MyBatis-Spring 相关依赖:
复制
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.6version>
dependency>
注:若使用非 Spring Boot 项目,需手动配置 SqlSessionFactoryBean 和 SqlSessionTemplate。
在 application.yml(Spring Boot)或 Spring XML 配置文件中定义数据源:
复制
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
通过 SqlSessionFactoryBean
创建 SqlSessionFactory
,并绑定数据源:
@Configuration
public class MyBatisConfig {
@Autowired
private DataSource dataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
// 指定 MyBatis 配置文件路径(可选)
factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
return factoryBean.getObject();
}
}
XML 配置示例:
复制
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
bean>
MyBatis 默认使用 SIMPLE 执行器
,每次操作生成独立 SQL 语句。而 BATCH 执行器
通过复用预编译语句(PreparedStatement)和合并参数,减少数据库交互次数,适用于批量插入/更新场景。
<configuration>
<settings>
<setting name="defaultExecutorType" value="BATCH"/>
settings>
configuration>
SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
try {
UserMapper mapper = batchSession.getMapper(UserMapper.class);
// 执行批量操作
batchSession.commit();
} finally {
batchSession.close();
}
@Configuration
@MapperScan(basePackages = "com.example.mapper") // 自动扫描 Mapper 接口
public class MyBatisBatchConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource)
throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
// 配置全局执行器为 BATCH
org.apache.ibatis.session.Configuration configuration =
new org.apache.ibatis.session.Configuration();
configuration.setDefaultExecutorType(ExecutorType.BATCH);
factoryBean.setConfiguration(configuration);
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
// 显式指定批处理模式
return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
}
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private SqlSessionTemplate batchSqlSession;
@Override
@Transactional
public void batchInsert(List<User> users) {
UserMapper mapper = batchSqlSession.getMapper(UserMapper.class);
mapper.insertBatch(users);
}
}
# application.yml
mybatis:
configuration:
default-executor-type: BATCH
map-underscore-to-camel-case: true
global-config:
db-config:
id-type: auto
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
int batchSize = 500;
SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = batchSession.getMapper(UserMapper.class);
for (int i = 0; i < users.size(); i++) {
mapper.insert(users.get(i));
if (i % batchSize == 0) {
batchSession.commit();
batchSession.clearCache(); // 清理缓存
}
}
batchSession.commit();
} finally {
batchSession.close();
}
通过 Java Config 方式可实现更细粒度的配置控制,结合 Spring Boot 的自动配置特性,既能保持配置简洁性,又能满足高性能批量操作需求。建议生产环境采用混合配置模式:基础配置使用 YAML,特殊场景通过 Java Config 覆盖。
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Override
public int syncChildDataListNew(PeoChildScreeningSyncDto dto) throws Exception {
int result = 0;
int errorCount = 0;
PeoChildScreening queryParams = new PeoChildScreening();
// 1. 分页参数
int pageSize = Optional.ofNullable(dto.getPageSize()).orElse(1000);
int total = peoChildScreeningMapper.selectPeoChildListCount(queryParams);
int totalPages = (total + pageSize - 1) / pageSize;
// 2. 批处理参数
final int BATCH_SIZE = 5;
try (SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
PeoChildScreeningMapper batchMapper = batchSession.getMapper(PeoChildScreeningMapper.class);
for (int pageNum = 1; pageNum <= totalPages; pageNum++) {
PageHelper.startPage(pageNum, pageSize);
List<PeoChildScreening> list = peoChildScreeningMapper.selectPeoChildList(queryParams);
PageHelper.clearPage();
if (CollectionUtils.isEmpty(list)) continue;
// 3. 批量处理逻辑
try {
// 3.1 批量查询旧数据(按childPeoBaseId批量查询)
String[] childIdsArray = list.stream()
.map(PeoChildScreening::getChildPeoBaseId)
.toArray(String[]::new);
PeoChildScreening query = new PeoChildScreening();
List<PeoChildScreening> oldList = batchMapper.selectPeoChildScreeningBatch(childIdsArray);// 改造后的批量查询方法
// 3.2 批量校验与处理
Map<String, PeoChildScreening> oldMap = oldList.stream()
.collect(Collectors.toMap(PeoChildScreening::getChildPeoBaseId, Function.identity()));
List<PeoChildScreening> updateList = new ArrayList<>();
List<PeoChildScreening> insertList = new ArrayList<>();
for (PeoChildScreening item : list) {
String childId = item.getChildPeoBaseId();
PeoChildScreening oldItem = oldMap.get(childId);
//儿童孩次查询
item.setGb302(String.valueOf(getChildOrder(item)));
// 校验逻辑(批量处理)
Map<String, Object> validateInfo = PeoChildScreeningUtil.validateInfo(item);
//校验信息
if(validateInfo.get("isValid") != null && (boolean) validateInfo.get("isValid")){
item.setSystemScreeningStatus("1");//通过
item.setSystemScreeningReason("通过");
}else {
item.setSystemScreeningStatus("2");//不通过
item.setSystemScreeningReason(validateInfo.get("errorMessage").toString());
}
item.setSystemScreeningTime(DateUtils.getTime());//系统核查时间
// 更新或插入判断
if (oldItem != null && StringUtils.isNotEmpty(oldItem.getId())) {
BeanCopyUtils.springCopyIgnoreNull(item, oldItem);
updateList.add(oldItem);
} else {
item.setId(IdUtils.simpleUUID());
item.setAuditStatus("2");//默认未审核
insertList.add(item);
}
}
// 3.3 批量提交
if (!updateList.isEmpty()) {
// 执行批量更新
for (PeoChildScreening peoChildScreening : updateList) {
batchMapper.updatePeoChildScreening(peoChildScreening);
}
}
if (!insertList.isEmpty()) {
// 执行批量插入
for (PeoChildScreening peoChildScreening : insertList) {
batchMapper.insertPeoChildScreening(peoChildScreening);
}
}
batchSession.commit();
result += list.size();
} catch (Exception e) {
batchSession.rollback();
errorCount += list.size();
log.error("第 {} 页处理失败", pageNum, e);
}
}
}
log.info("同步完成 - 成功: {}, 失败: {}", result, errorCount);
return result;
}