1. MyBatis入门基础与核心概念
MyBatis作为Java生态中最受欢迎的持久层框架之一,其设计哲学与Hibernate等全自动ORM框架有着本质区别。我在实际项目中使用MyBatis已有7年时间,见证了这个框架从iBatis到MyBatis 3.x的演进过程。对于刚接触MyBatis的开发者而言,理解其核心工作模式是掌握这个框架的关键。
MyBatis的核心优势在于它完美平衡了SQL控制权与开发效率。与Hibernate等全自动ORM不同,MyBatis不会尝试完全隐藏SQL,而是让开发者保留对SQL的完整控制权,同时通过智能的参数映射和结果集映射减少重复代码。这种设计使得MyBatis特别适合需要精细控制SQL性能的场景,比如金融交易系统、大数据量批处理等对性能敏感的应用。
重要提示:MyBatis 3.5+版本在动态SQL处理、类型处理器等方面有显著改进,建议新项目直接使用3.7.x最新稳定版
1.1 基础环境搭建
创建一个基本的MyBatis项目需要以下组件:
- 核心依赖:mybatis.jar(当前最新版为3.7.0)
- 数据库驱动:如mysql-connector-java
- 连接池:推荐HikariCP或Druid
Maven配置示例:
XML
3
<groupId>org.mybatis</groupId>
4
<artifactId>mybatis</artifactId>
5
<version>3.7.0</version>
8
<groupId>mysql</groupId>
9
<artifactId>mysql-connector-java</artifactId>
10
<version>8.0.28</version>
13
<groupId>com.zaxxer</groupId>
14
<artifactId>HikariCP</artifactId>
15
<version>5.0.1</version>
1.2 核心配置文件解析
mybatis-config.xml是MyBatis的主配置文件,典型结构如下:
XML
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
3
"http://mybatis.org/dtd/mybatis-3-config.dtd">
5
<properties resource="db.properties"/>
8
<setting name="cacheEnabled" value="true"/>
9
<setting name="lazyLoadingEnabled" value="false"/>
13
<package name="com.example.model"/>
16
<environments default="development">
17
<environment id="development">
18
<transactionManager type="JDBC"/>
19
<dataSource type="POOLED">
20
<property name="driver" value="${jdbc.driver}"/>
21
<property name="url" value="${jdbc.url}"/>
22
<property name="username" value="${jdbc.username}"/>
23
<property name="password" value="${jdbc.password}"/>
29
<mapper resource="mapper/UserMapper.xml"/>
关键配置项说明:
- environments:支持多环境配置,通过default属性切换
- dataSource:连接池配置,生产环境建议使用外部连接池
- typeAliases:为Java类创建别名,避免全限定名重复书写
- mappers:指定Mapper文件位置
2. MyBatis核心组件与工作流程
2.1 SQL会话工厂构建
SqlSessionFactory是MyBatis的核心接口,每个数据库对应一个实例,构建方式:
JAVA
1
String resource = "mybatis-config.xml";
2
InputStream inputStream = Resources.getResourceAsStream(resource);
3
SqlSessionFactory sqlSessionFactory =
4
new SqlSessionFactoryBuilder().build(inputStream);
性能提示:SqlSessionFactory构建成本较高,应用生命周期内应保持单例
2.2 Mapper XML开发模式
UserMapper.xml示例展示了MyBatis的核心映射功能:
XML
1
<mapper namespace="com.example.mapper.UserMapper">
2
<resultMap id="userResultMap" type="User">
3
<id property="id" column="user_id"/>
4
<result property="username" column="user_name"/>
5
<result property="email" column="user_email"/>
8
<select id="selectUser" resultMap="userResultMap">
9
SELECT * FROM users WHERE user_id = #{id}
12
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
13
INSERT INTO users(user_name, user_email)
14
VALUES(#{username}, #{email})
关键元素解析:
- resultMap:定义结果集映射规则,解决列名与属性名不一致问题
- #{param}:参数占位符,MyBatis会进行预编译处理防止SQL注入
- useGeneratedKeys:获取数据库生成的主键值
2.3 动态SQL实践
MyBatis提供了强大的动态SQL支持:
XML
1
<select id="findUsers" resultType="User">
4
<if test="username != null">
5
AND user_name LIKE CONCAT('%', #{username}, '%')
7
<if test="email != null">
8
AND user_email = #{email}
10
<if test="ids != null and ids.size() > 0">
12
<foreach item="id" collection="ids"
13
open="(" separator="," close=")">
18
ORDER BY create_time DESC
动态SQL元素包括:
- if:条件判断
- choose/when/otherwise:多路选择
- foreach:集合遍历
- bind:创建变量
3. 高级特性与最佳实践
3.1 缓存机制深度解析
MyBatis提供两级缓存:
- 一级缓存:SqlSession级别,默认开启
- 二级缓存:Mapper级别,需手动配置
配置二级缓存:
XML
1
<mapper namespace="com.example.mapper.UserMapper">
2
<cache eviction="LRU" flushInterval="60000"
3
size="512" readOnly="true"/>
事务陷阱:在Spring管理的事务中,一级缓存可能导致读取到旧数据,可通过@Options(flushCache=Options.FlushCachePolicy.TRUE)强制刷新
3.2 类型处理器扩展
自定义类型处理器示例(处理Java 8的LocalDateTime):
JAVA
1
@MappedTypes(LocalDateTime.class)
2
@MappedJdbcTypes(JdbcType.TIMESTAMP)
3
public class LocalDateTimeTypeHandler
4
extends BaseTypeHandler<LocalDateTime> {
7
public void setNonNullParameter(PreparedStatement ps, int i,
8
LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
9
ps.setTimestamp(i, Timestamp.valueOf(parameter));
13
public LocalDateTime getNullableResult(ResultSet rs, String columnName)
15
Timestamp timestamp = rs.getTimestamp(columnName);
16
return timestamp != null ? timestamp.toLocalDateTime() : null;
注册自定义处理器:
XML
2
<typeHandler handler="com.example.handler.LocalDateTimeTypeHandler"/>
3.3 MyBatis与Spring Boot整合
Spring Boot Starter配置示例:
YAML
2
mapper-locations: classpath:mapper/**/*.xml
3
type-aliases-package: com.example.model
5
map-underscore-to-camel-case: true
6
default-fetch-size: 100
7
default-statement-timeout: 30
常见整合问题解决方案:
- 多数据源配置:使用@MapperScan的sqlSessionFactoryRef属性
- 事务管理:配合@Transactional注解使用
- 分页插件:集成PageHelper
4. 生产环境实战经验
4.1 性能优化方案
经过多个高并发项目验证的有效优化手段:
-
SQL优化:
- 避免SELECT *,只查询需要的列
- 合理使用索引提示
- 批量操作使用foreach标签
-
配置优化:
XML
2
<setting name="defaultExecutorType" value="BATCH"/>
3
<setting name="jdbcTypeForNull" value="NULL"/>
-
连接池配置(以HikariCP为例):
PROPERTIES
1
spring.datasource.hikari.maximum-pool-size=20
2
spring.datasource.hikari.connection-timeout=30000
3
spring.datasource.hikari.idle-timeout=600000
4.2 常见问题排查指南
| 问题现象 |
可能原因 |
解决方案 |
| 查询结果与预期不符 |
一级缓存未刷新 |
设置flushCache=true |
| 参数绑定失败 |
参数名不匹配 |
使用@Param注解明确参数名 |
| XML文件未加载 |
资源路径错误 |
检查mappers配置和文件位置 |
| 动态SQL解析错误 |
特殊字符未转义 |
使用CDATA包裹或转义符号 |
4.3 MyBatis-Plus进阶使用
MyBatis-Plus是MyBatis的增强工具,典型配置:
JAVA
2
@MapperScan("com.example.mapper")
3
public class MybatisPlusConfig {
6
public MybatisPlusInterceptor mybatisPlusInterceptor() {
7
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
8
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
9
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
核心功能对比:
| 功能 |
MyBatis |
MyBatis-Plus |
| CRUD操作 |
需手动编写 |
内置通用Mapper |
| 分页支持 |
需插件 |
内置分页插件 |
| 代码生成 |
需额外工具 |
内置代码生成器 |
| 乐观锁 |
需手动实现 |
内置支持 |
5. 架构设计与扩展思路
5.1 插件开发实践
自定义插件示例(SQL执行时间统计):
JAVA
2
@Signature(type= StatementHandler.class,
4
args={Statement.class, ResultHandler.class}),
5
@Signature(type= StatementHandler.class,
7
args={Statement.class})
9
public class SqlCostInterceptor implements Interceptor {
12
public Object intercept(Invocation invocation) throws Throwable {
13
long start = System.currentTimeMillis();
15
return invocation.proceed();
17
long cost = System.currentTimeMillis() - start;
18
String sqlId = getSqlId(invocation);
19
System.out.println("SQL [" + sqlId + "] cost: " + cost + "ms");
23
private String getSqlId(Invocation invocation) {
24
MappedStatement ms = (MappedStatement)
25
invocation.getArgs()[0].getClass()
26
.getMethod("getMappedStatement").invoke(invocation.getArgs()[0]);
5.2 多租户方案实现
基于MyBatis的多租户架构设计:
-
方案一:动态数据源切换
- 优点:完全隔离,安全性高
- 缺点:连接池数量多,资源消耗大
-
方案二:SQL拦截改写
JAVA
1
public class TenantInterceptor implements Interceptor {
3
public Object intercept(Invocation invocation) throws Throwable {
5
String tenantId = TenantContext.getCurrentTenant();
8
BoundSql boundSql = ((StatementHandler)invocation.getTarget())
10
String newSql = boundSql.getSql() + " AND tenant_id = '"
14
Field field = boundSql.getClass().getDeclaredField("sql");
15
field.setAccessible(true);
16
field.set(boundSql, newSql);
18
return invocation.proceed();
5.3 分布式事务集成
与Seata集成的关键配置:
PROPERTIES
2
seata.tx-service-group=my_test_tx_group
3
seata.service.vgroup-mapping.my_test_tx_group=default
4
seata.enable-auto-data-source-proxy=true
MyBatis配置调整:
JAVA
2
public class DataSourceConfig {
5
@ConfigurationProperties(prefix = "spring.datasource")
6
public DruidDataSource druidDataSource() {
7
return new DruidDataSource();
12
public DataSource dataSource(DruidDataSource druidDataSource) {
13
return new DataSourceProxy(druidDataSource);
在多年MyBatis使用经验中,我发现框架本身只是工具,真正的价值在于如何根据业务特点设计合理的持久层架构。对于新项目,建议从MyBatis-Plus开始,可以显著提升开发效率;对于已有MyBatis项目,渐进式引入插件和自定义组件是更稳妥的演进路线。