MyBatis-Plus XML 与 注解混合使用指南

适用版本: MyBatis-Plus 3.0+(主流版本兼容)
适用场景: 简单 CRUD 用注解,复杂 SQL 用 XML,兼顾开发效率与灵活性。


一、核心概念

1.1 MyBatis-Plus 简介

MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,简化 CRUD 操作,提供 BaseMapper<T> 接口,无需编写 XML 即可完成基础操作。

1.2 注解 vs XML

特性 注解(Annotation) XML 映射文件
开发速度 ⚡ 快(一行搞定) ⏳ 慢(需写文件)
可读性 简单 SQL 清晰 复杂 SQL 更易读
动态 SQL 有限支持 完全支持(<if>, <choose>等)
维护性 小项目好 大项目更易维护
混合使用 ✅ 支持 ✅ 支持

1.3 混合使用原则

  • 简单 CRUD:使用 @Select, @Insert, @Update, @Delete 注解
  • 复杂查询(多表、动态条件、分页、子查询等):使用 XML 文件
  • XML 优先级高于注解:同名方法在 XML 中定义后,注解失效。

二、详细操作步骤(手把手教学)

步骤 1:添加依赖(Maven)

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MyBatis-Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>

    <!-- 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Lombok(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

步骤 2:配置 application.yml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  # XML 映射文件路径
  mapper-locations: classpath:mapper/*.xml
  # 实体类包路径(自动识别 @TableName)
  type-aliases-package: com.example.demo.entity
  configuration:
    # 开启驼峰命名转换
    map-underscore-to-camel-case: true
    # 打印 SQL(开发环境)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

步骤 3:创建实体类(Entity)

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

@Data
@TableName("user") // 指定表名
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;

    @TableField("name")
    private String name;

    @TableField("email")
    private String email;

    @TableField(exist = false) // 非数据库字段
    private String remark;
}

步骤 4:创建 Mapper 接口

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface UserMapper extends BaseMapper<User> {

    // 注解方式:简单查询
    @Select("SELECT * FROM user WHERE name = #{name}")
    User selectByName(@Param("name") String name);

    // 注解方式:更新
    @Update("UPDATE user SET email = #{email} WHERE name = #{name}")
    int updateEmailByName(@Param("name") String name, @Param("email") String email);

    // XML 方式:复杂查询(分页 + 多条件)
    IPage<User> selectUserPage(Page<User> page, @Param("query") UserQuery query);
}

步骤 5:创建 XML 映射文件

resources/mapper/UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">

    <!-- 复杂分页查询 -->
    <select id="selectUserPage" resultType="com.example.demo.entity.User">
        SELECT id, name, email
        FROM user
        <where>
            <if test="query != null">
                <if test="query.name != null and query.name != ''">
                    AND name LIKE CONCAT('%', #{query.name}, '%')
                </if>
                <if test="query.email != null and query.email != ''">
                    AND email = #{query.email}
                </if>
            </if>
        </where>
        ORDER BY id DESC
    </select>

</mapper>

💡 注意:namespace 必须是 Mapper 接口的全限定名。


步骤 6:创建查询条件类(可选)

import lombok.Data;

@Data
public class UserQuery {
    private String name;
    private String email;
}

步骤 7:Service 层调用示例

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    // 使用注解查询
    public User getUserByName(String name) {
        return userMapper.selectByName(name);
    }

    // 使用 XML 分页查询
    public IPage<User> getUserPage(int pageNum, int pageSize, UserQuery query) {
        Page<User> page = new Page<>(pageNum, pageSize);
        return userMapper.selectUserPage(page, query);
    }
}

步骤 8:Controller 测试

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/byname/{name}")
    public User getUser(@PathVariable String name) {
        return userService.getUserByName(name);
    }

    @GetMapping("/page")
    public IPage<User> getPage(
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            UserQuery query) {
        return userService.getUserPage(pageNum, pageSize, query);
    }
}

三、常见错误与解决方案

错误现象 原因 解决方案
Invalid bound statement (not found) XML 文件未被扫描 检查 mapper-locations 路径是否正确
Property 'sqlSessionFactory' or 'sqlSessionTemplate' is required 未启用 MP 自动配置 确保主类加 @SpringBootApplication
注解 SQL 不生效 XML 中定义了同名方法 删除 XML 方法或注解方法
参数未映射(null) 未使用 @Param 多参数必须加 @Param
表名或字段名映射错误 未开启驼峰转换 map-underscore-to-camel-case: true

四、注意事项

  1. 命名空间必须一致:XML 中 namespace = Mapper 接口全路径。
  2. 方法名必须一致:XML 中 <select id="xxx"> 必须与接口方法名相同。
  3. 参数绑定:使用 @Param("xxx") 显式命名参数,尤其多参数或复杂对象。
  4. 返回类型一致:XML resultType 必须与接口返回类型匹配。
  5. XML 优先级更高:一旦 XML 定义了方法,注解将被忽略。

五、使用技巧

5.1 使用 @Results 注解处理复杂映射

@Results({
    @Result(property = "id", column = "id", id = true),
    @Result(property = "name", column = "user_name")
})
@Select("SELECT id, user_name FROM user WHERE id = #{id}")
User selectByIdCustom(Long id);

5.2 利用 MP 内置方法减少 XML

// 直接使用 BaseMapper 方法,无需写 XML
List<User> users = userMapper.selectList(
    new QueryWrapper<User>().like("name", "张").eq("status", 1)
);

5.3 动态 SQL 用 XML,静态用注解

保持代码清晰,避免在注解中拼接大量 SQL 字符串。


六、最佳实践

实践 说明
✅ 简单 CRUD 用注解 提高开发效率
✅ 复杂查询用 XML 易维护、支持动态 SQL
✅ 统一 XML 路径 mapper/*.xml
✅ 使用 @Param 避免参数绑定错误
✅ 开启驼峰转换 避免字段映射问题
✅ 分页使用 MP 分页插件 避免手写分页 SQL

七、性能优化建议

  1. 避免 N+1 查询:使用 JOIN 一次性查出关联数据,而非循环查数据库。
  2. 合理使用缓存:开启 MyBatis 二级缓存(@CacheNamespace)。
  3. SQL 优化:为查询字段加索引,避免 SELECT *
  4. 分页优化:大数据量使用 keySet 分页游标分页,避免 LIMIT offset, size 性能下降。
  5. 批量操作:使用 saveBatch()updateBatchById() 批量处理。

八、总结

使用方式 适用场景 推荐指数
注解 简单查询、更新、插入 ⭐⭐⭐⭐☆
XML 复杂查询、动态 SQL、多表联查 ⭐⭐⭐⭐⭐
混合使用 大多数企业项目 ✅ 强烈推荐

结论:注解 + XML 混合使用是 MyBatis-Plus 的黄金组合,兼顾开发效率与灵活性。