MyBatis常见问题解决方案与性能优化指南
MyBatis常见问题与解决方案实战指南
作为Java开发中最流行的ORM框架之一,MyBatis在实际应用中经常会遇到一些典型问题。本文将深入分析5个最常见的技术挑战,并提供经过生产验证的解决方案。
1. N+1查询问题:性能杀手与解决方案
问题本质
N+1查询问题是指当查询主对象时,对每个主对象关联的子对象都产生一次额外查询。例如查询10个订单,每个订单又查询一次订单项,总共产生11次查询(1次查订单+10次查订单项)。
解决方案
方案一:使用JOIN查询+结果映射
<resultMap id="orderWithItems" type="Order">
<id property="id" column="order_id"/>
<collection property="items" ofType="OrderItem"
resultMap="orderItemResultMap"/>
</resultMap>
<select id="getOrdersWithItems" resultMap="orderWithItems">
SELECT o.*, i.*
FROM orders o LEFT JOIN order_items i ON o.id = i.order_id
WHERE o.user_id = #{userId}
</select>
方案二:开启全局延迟加载
<!-- mybatis-config.xml -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
实践建议:
- 对于关联数据量小的场景使用JOIN查询
- 大数据量关联时使用延迟加载+按需加载
- 监控SQL日志,使用
@FetchSize
优化大批量查询
2. 事务管理:Spring集成最佳实践
声明式事务配置
@Configuration
@EnableTransactionManagement
public class MyBatisConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
编程式事务示例
public void batchProcessOrders(List<Order> orders) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(status -> {
try {
orders.forEach(orderMapper::update);
return true;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
常见陷阱:
- 方法内部调用导致
@Transactional
失效 - 错误的事务传播级别设置
- MyBatis一级缓存与事务边界不一致
最佳实践:
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderMapper orderMapper;
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30)
public void processOrder(Order order) {
// 业务逻辑
}
}
3. 主键生成策略全解析
常用策略对比
策略类型 | 示例配置 | 适用场景 | 优缺点 |
---|---|---|---|
数据库自增 | useGeneratedKeys="true" | MySQL、PostgreSQL | 简单但批量插入效率低 |
UUID | 应用层生成 | 分布式系统 | 全局唯一但无序 |
序列(Sequence) | <selectKey> 调用序列 | Oracle | 高并发下可能有性能瓶颈 |
雪花算法 | 自定义TypeHandler | 分布式系统 | 趋势递增但依赖系统时钟 |
典型配置示例
数据库自增ID:
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users(name) VALUES(#{name})
</insert>
Oracle序列:
<insert id="insertProduct">
<selectKey keyProperty="id" resultType="long" order="BEFORE">
SELECT product_seq.nextval FROM dual
</selectKey>
INSERT INTO products(id, name) VALUES(#{id}, #{name})
</insert>
批量插入优化:
// 使用BatchExecutor
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for (User user : userList) {
mapper.insert(user);
}
session.commit();
}
4. 分页实现:从基础到高级
物理分页 vs 逻辑分页
PageHelper实现方案
// Spring Boot配置
@Configuration
public class MyBatisConfig {
@Bean
public PageInterceptor pageInterceptor() {
return new PageInterceptor();
}
}
// 使用示例
public PageInfo<User> getUsers(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectAll();
return new PageInfo<>(users);
}
手写分页SQL(MySQL)
<select id="selectByPage" resultType="User">
SELECT * FROM users
ORDER BY create_time DESC
LIMIT #{offset}, #{pageSize}
</select>
性能优化建议:
- 避免
COUNT(*)
全表扫描,使用覆盖索引 - 大数据量分页使用"seek method"(where id > ? limit ?)
- 分页结果缓存策略设计
5. 多数据源处理:企业级解决方案
动态数据源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
// 使用AOP切换数据源
@Around("@annotation(dataSource)")
public Object switchDataSource(ProceedingJoinPoint pjp, DataSource dataSource) throws Throwable {
DataSourceContextHolder.set(dataSource.value());
try {
return pjp.proceed();
} finally {
DataSourceContextHolder.clear();
}
}
Spring Boot多数据源配置
# application.yml
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/db1
username: user1
password: pass1
secondary:
url: jdbc:mysql://localhost:3306/db2
username: user2
password: pass2
事务管理要点:
- 使用
@Transactional(transactionManager = "primaryTransactionManager")
指定事务管理器 - 避免跨数据源事务(考虑分布式事务方案)
- 连接池参数根据业务特点单独配置
总结与进阶建议
- 性能监控:集成Micrometer或自定义拦截器监控SQL性能
- 安全审计:定期检查SQL注入风险点,特别是动态SQL部分
版本升级:关注MyBatis新特性如:
- 3.5+版本的Kotlin DSL支持
- 增强的批量操作API
- 改进的延迟加载机制
通过合理应用这些解决方案,可以显著提升MyBatis在生产环境中的稳定性和性能表现。建议根据具体业务场景进行组合使用,并建立相应的监控机制。