一、MyBatis核心架构解析
1. 什么是MyBatis?
MyBatis是一款半自动ORM框架,它通过XML或注解将SQL与Java对象映射,提供比Hibernate更灵活的SQL控制能力,同时消除了传统JDBC的样板代码。
2. 核心组件关系图
3. 核心组件职责
组件 | 职责 | 生命周期 |
---|---|---|
SqlSessionFactory | 创建SqlSession | 应用级单例 |
SqlSession | 执行SQL操作 | 请求/方法级 |
Mapper接口 | 定义数据库操作 | 应用级 |
SQL映射文件 | 配置SQL语句 | 应用级 |
二、环境搭建与配置
1. Maven依赖
<dependencies>
<!-- MyBatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
</dependencies>
2. 核心配置文件(mybatis-config.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 开启驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<package name="com.example.model"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
<!-- HikariCP连接池配置 -->
<property name="maximumPoolSize" value="20"/>
<property name="connectionTimeout" value="30000"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
三、CRUD操作详解
1. 实体类定义
public class User {
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
// 构造器、getter/setter省略
}
2. Mapper接口
public interface UserMapper {
// 插入操作返回自增主键
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user);
int updateUser(User user);
int deleteUser(Long id);
User selectUserById(Long id);
List<User> selectAllUsers();
}
3. XML映射文件(UserMapper.xml)
<mapper namespace="com.example.mapper.UserMapper">
<!-- 基础结果映射 -->
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
<result property="createTime" column="create_time"/>
</resultMap>
<!-- 插入用户 -->
<insert id="insertUser" parameterType="User">
INSERT INTO users(username, email, create_time)
VALUES(#{username}, #{email}, #{createTime})
</insert>
<!-- 更新用户 -->
<update id="updateUser" parameterType="User">
UPDATE users SET
username = #{username},
email = #{email}
WHERE id = #{id}
</update>
<!-- 查询用户 -->
<select id="selectUserById" resultMap="userResultMap">
SELECT * FROM users WHERE id = #{id}
</select>
<!-- 分页查询 -->
<select id="selectAllUsers" resultMap="userResultMap">
SELECT * FROM users LIMIT #{offset}, #{pageSize}
</select>
</mapper>
四、参数处理与动态SQL
<select id="selectUsersByCondition" resultMap="userResultMap">
SELECT * FROM users
WHERE 1=1
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null">
AND email = #{email}
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
</select>
1. 多参数处理
// Mapper接口方法
List<User> selectUsersByCondition(
@Param("username") String username,
@Param("email") String email,
@Param("startTime") LocalDateTime startTime);
<select id="selectUsersByCondition" resultMap="userResultMap">
SELECT * FROM users
WHERE 1=1
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null">
AND email = #{email}
</if>
<if test="startTime != null">
AND create_time >= #{startTime}
</if>
</select>
2. 复杂动态SQL
<!-- 批量插入 -->
<insert id="batchInsertUsers" parameterType="list">
INSERT INTO users(username, email) VALUES
<foreach item="user" collection="list" separator=",">
(#{user.username}, #{user.email})
</foreach>
</insert>
<!-- 动态更新 -->
<update id="updateUserSelective" parameterType="User">
UPDATE users
<set>
<if test="username != null">username=#{username},</if>
<if test="email != null">email=#{email},</if>
</set>
WHERE id=#{id}
</update>
<!-- 多条件选择 -->
<select id="findActiveUsers" resultMap="userResultMap">
SELECT * FROM users
<where>
<choose>
<when test="status == 'active'">
AND status = 1
</when>
<when test="status == 'inactive'">
AND status = 0
</when>
<otherwise>
AND status IS NOT NULL
</otherwise>
</choose>
</where>
</select>
五、高级结果映射
1. 一对一关联
public class Order {
private Long id;
private String orderNo;
private User user; // 一对一关联
}
<resultMap id="orderResultMap" type="Order">
<id property="id" column="id"/>
<result property="orderNo" column="order_no"/>
<!-- 一对一关联映射 -->
<association property="user" javaType="User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
</association>
</resultMap>
<select id="selectOrderWithUser" resultMap="orderResultMap">
SELECT o.*, u.username, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.id = #{id}
</select>
2. 一对多关联
public class Department {
private Long id;
private String name;
private List<Employee> employees; // 一对多关联
}
<resultMap id="deptResultMap" type="Department">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!-- 一对多集合映射 -->
<collection property="employees" ofType="Employee">
<id property="id" column="emp_id"/>
<result property="name" column="emp_name"/>
<result property="position" column="position"/>
</collection>
</resultMap>
<select id="selectDepartmentWithEmployees" resultMap="deptResultMap">
SELECT d.*, e.id AS emp_id, e.name AS emp_name, e.position
FROM departments d
LEFT JOIN employees e ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
六、缓存机制深度优化
1. 缓存层次结构
2. 缓存配置策略
<!-- 在Mapper.xml中配置二级缓存 -->
<cache
eviction="FIFO" <!-- 回收策略:FIFO/LRU/SOFT/WEAK -->
flushInterval="60000" <!-- 刷新间隔(毫秒) -->
size="512" <!-- 缓存对象数量 -->
readOnly="true"/> <!-- 是否只读 -->
<!-- 使用自定义缓存实现 -->
<cache type="com.example.cache.RedisCache"/>
3. 缓存使用注意事项
// 手动清除缓存
sqlSession.clearCache(); // 清除一级缓存
// 在Statement中控制缓存
<select id="selectUsers" useCache="false" flushCache="true">
SELECT * FROM users
</select>
七、Spring Boot整合实践
1. 快速整合配置
# application.yml
mybatis:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: com.example.model
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
lazy-loading-enabled: true
2. 自动注入Mapper
@Mapper
public interface UserMapper {
// 方法定义
}
// 在Service中使用
@Service
public class UserService {
private final UserMapper userMapper;
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
public User getUserById(Long id) {
return userMapper.selectUserById(id);
}
}
3. 分页插件集成
// 配置分页插件
@Configuration
public class MyBatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
// 使用分页
public Page<User> getUsersByPage(int pageNum, int pageSize) {
Page<User> page = new Page<>(pageNum, pageSize);
return userMapper.selectPage(page, null);
}
八、性能优化与最佳实践
1. SQL优化技巧
/* 避免SELECT * */
SELECT id, username, email FROM users
/* 使用覆盖索引 */
CREATE INDEX idx_username ON users(username);
/* 批量操作替代循环 */
<foreach item="item" collection="list" separator=",">
(#{item.value})
</foreach>
2. 事务管理
@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
// 减少转出账户余额
userMapper.deductBalance(fromId, amount);
// 增加转入账户余额
userMapper.addBalance(toId, amount);
// 记录交易日志
logMapper.insertTransferLog(fromId, toId, amount);
}
3. 监控与诊断
# 开启SQL日志
logging:
level:
com.example.mapper: debug
-- 使用EXPLAIN分析查询
EXPLAIN SELECT * FROM users WHERE username = 'john';
九、高级特性应用
1. 存储过程调用
<select id="callUserProcedure" statementType="CALLABLE">
{call get_user_by_id(#{id, mode=IN}, #{username, mode=OUT, jdbcType=VARCHAR})}
</select>
2. 类型处理器
// 自定义枚举处理器
public class StatusTypeHandler extends BaseTypeHandler<Status> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Status parameter, JdbcType jdbcType) {
ps.setInt(i, parameter.getCode());
}
// 其他方法实现...
}
// 注册处理器
<typeHandlers>
<typeHandler handler="com.example.handler.StatusTypeHandler"/>
</typeHandlers>
3. 插件开发(拦截器)
@Intercepts({
@Signature(type = Executor.class, method = "update",
args = {MappedStatement.class, Object.class})
})
public class SqlCostInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long end = System.currentTimeMillis();
System.out.println("SQL执行耗时: " + (end - start) + "ms");
return result;
}
}
// 注册插件
<plugins>
<plugin interceptor="com.example.plugin.SqlCostInterceptor"/>
</plugins>
十、实战案例:电商订单系统
1. 复杂查询示例
<select id="selectOrderDetails" resultMap="orderDetailMap">
SELECT
o.id, o.order_no, o.total_amount,
u.id AS user_id, u.username, u.email,
p.id AS product_id, p.name AS product_name, p.price,
oi.quantity
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.status = 'COMPLETED'
<if test="startDate != null">
AND o.create_time >= #{startDate}
</if>
<if test="minAmount != null">
AND o.total_amount >= #{minAmount}
</if>
ORDER BY o.create_time DESC
</select>
2. 事务边界设计
@Service
public class OrderService {
private final OrderMapper orderMapper;
private final InventoryService inventoryService;
private final PaymentService paymentService;
@Transactional(rollbackFor = Exception.class)
public void placeOrder(Order order) {
// 1. 创建订单
orderMapper.insertOrder(order);
// 2. 扣减库存
inventoryService.deductStock(order.getItems());
// 3. 支付处理
paymentService.processPayment(order);
// 4. 更新订单状态
orderMapper.updateOrderStatus(order.getId(), "PAID");
}
}
结语:MyBatis在企业级应用中的定位
MyBatis作为持久层框架的选择标准:
适用场景:
需要精细控制SQL的场景
复杂报表查询
遗留数据库系统改造
性能考量:
与Hibernate相比,在复杂查询上通常有更好表现
对数据库特性利用更充分
学习建议:
掌握动态SQL编写技巧
深入理解结果集映射机制
合理应用缓存策略
结合Spring事务管理
"不要试图用ORM解决所有问题,MyBatis的价值在于平衡SQL控制力和开发效率"
—— MyBatis创始人 Clinton Begin