深入掌握MyBatis:核心解析

发布于:2025-06-26 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、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作为持久层框架的选择标准:

  1. 适用场景

    • 需要精细控制SQL的场景

    • 复杂报表查询

    • 遗留数据库系统改造

  2. 性能考量

    • 与Hibernate相比,在复杂查询上通常有更好表现

    • 对数据库特性利用更充分

  3. 学习建议

    • 掌握动态SQL编写技巧

    • 深入理解结果集映射机制

    • 合理应用缓存策略

    • 结合Spring事务管理

"不要试图用ORM解决所有问题,MyBatis的价值在于平衡SQL控制力和开发效率"
—— MyBatis创始人 Clinton Begin


网站公告

今日签到

点亮在社区的每一天
去签到