在 Java 后端开发中,MyBatis 以其 灵活的 SQL 控制 和 低侵入性,始终是最主流的持久层框架之一。Spring Boot 通过 MyBatis-Spring-Boot-Starter 提供了“开箱即用”的自动化配置,让整合过程变得极为简单。而面对 MyBatis 最核心的两种开发方式——注解方式 与 XML 配置方式,开发者应如何根据业务场景做出选择?本文将从环境配置讲起,详细解析两种方式的核心用法、适用场景,并深入探讨 MyBatis 的灵魂功能:动态 SQL。
一、快速整合:MyBatis-Spring-Boot-Starter 配置
得益于 Spring Boot 强大的自动配置机制,整合 MyBatis 无需再编写繁琐的 XML 配置文件,仅需简单几步即可完成项目搭建。
1. 核心依赖引入
在项目的 pom.xml 文件中,引入 MyBatis-Spring-Boot-Starter 及相应的数据库驱动依赖。Spring Boot 会自动管理 MyBatis 核心包、Spring 整合包等相关依赖的版本。
<dependencies>
<!-- Spring Boot Web依赖(可选,用于接口测试) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Spring Boot Starter核心依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version> <!-- 版本需适配Spring Boot,请根据实际情况调整 -->
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(用于简化实体类代码,可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2. 核心配置参数
在 application.yml 中配置数据库连接信息及 MyBatis 专属参数,这是控制 MyBatis 行为的关键。
spring:
# 数据库连接配置
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
# 1. XML方式核心配置:指定Mapper XML文件路径(默认扫描resources/mybatis/mapper/**)
mapper-locations: classpath:mybatis/mapper/*.xml
# 2. 配置别名:指定实体类所在包,XML中可直接使用类名(无需全限定名)
type-aliases-package: com.example.mybatis.entity
# 3. 驼峰命名自动转换(数据库下划线命名 -> Java驼峰命名)
configuration:
map-underscore-to-camel-case: true
# 打印SQL语句(开发环境开启,便于调试)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
关键配置说明:
- mapper-locations:仅用于 XML 方式,指定 Mapper XML 文件的存放路径,支持通配符。
- type-aliases-package:配置后,在 XML 映射文件中引用实体类时可直接写
User,而无需写全限定名 com.example.mybatis.entity.User。
- map-underscore-to-camel-case:开启后,数据库表的
user_name 字段会自动映射到 Java 实体类的 userName 属性,极大减少了手动编写结果映射的工作。
3. 核心注解:@Mapper 与 @MapperScan
配置完成后,需要让 Spring 容器能够扫描并管理 MyBatis 的 Mapper 接口。这里有两种常用方式:
- @Mapper 注解:直接在 Mapper 接口上添加。适用于 Mapper 接口数量较少的项目。
- @MapperScan 注解:在 Spring Boot 主启动类上添加,指定 Mapper 接口所在的包路径,进行批量扫描。这是更推荐的方式,尤其适用于大型项目。
以下是 @MapperScan 的用法示例:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mybatis.mapper") // 扫描指定包下的所有Mapper接口
public class MyBatisApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisApplication.class, args);
}
}
二、两种开发方式对比:注解方式 vs XML 配置方式
MyBatis 提供了注解和 XML 两种将 SQL 与 Java 代码关联的模式,其核心差异在于 SQL 的存放位置。选择哪种方式,直接关系到代码的可读性、可维护性和开发效率。
1. 注解方式:简洁高效,适合简单 SQL
注解方式直接将 SQL 语句编写在 Mapper 接口方法的注解中,无需额外的 XML 文件,使得开发流程非常高效。
(1)核心注解及用法
常用注解与 XML 标签一一对应:
@Select:对应 <select> 标签,用于查询操作。
@Insert:对应 <insert> 标签,用于新增操作。
@Update:对应 <update> 标签,用于更新操作。
@Delete:对应 <delete> 标签,用于删除操作。
@Results:用于自定义结果集映射(开启驼峰转换后通常可省略)。
@Param:为方法参数指定名称,以便在 SQL 中引用。
(2)核心优势与适用场景
- 优势:开发快捷,SQL 与 Java 代码位于同一文件,一目了然;减少了文件数量。
- 劣势:编写复杂 SQL(如多表关联、动态条件)时,注解内容会变得冗长混乱,可读性和可维护性急剧下降。
- 适用场景:简单的单表 CRUD 操作、业务逻辑不复杂的查询。
2. XML 配置方式:灵活强大,适合复杂 SQL
XML 方式将 SQL 独立存放在 .xml 文件中,通过结构化的标签来组织 SQL 逻辑,是 MyBatis 最经典、功能最全面的使用方式。
(1)核心 XML 标签
除了基础的增删改查标签,XML 方式提供了更强大的标签集:
- 基础标签:
<select>, <insert>, <update>, <delete>。
- 结果映射标签:
<resultMap>,支持复杂的对象关联映射(一对一、一对多)。
- 动态 SQL 标签:
<if>, <where>, <foreach>, <choose> 等,这是 XML 方式的核心竞争力。
(2)核心优势与适用场景
- 优势:SQL 与 Java 代码完全解耦,便于 DBA 评审或统一管理;原生支持强大、直观的动态 SQL;格式工整,可读性极佳。
- 劣势:需要额外创建和维护 XML 文件,开发步骤稍多。
- 适用场景:复杂的多表关联查询、包含大量动态条件的搜索、批量操作、存储过程调用等。对于生产环境的复杂项目,强烈推荐使用 XML 方式。
3. 两种方式核心对比表
| 特性 |
注解方式 |
XML 配置方式 |
| SQL 存放位置 |
Java 接口的注解中 |
独立的 XML 文件中 |
| 开发效率 |
高,无需维护 XML |
稍低,需维护额外文件 |
| 复杂 SQL 支持 |
弱,编写繁琐、可读性差 |
强,原生支持动态 SQL、多表关联 |
| 代码解耦 |
差,SQL 与 Java 代码耦合 |
好,SQL 与 Java 代码完全分离 |
| 可读性 |
简单 SQL 尚可,复杂 SQL 差 |
优,格式规范,逻辑清晰 |
| 推荐适用场景 |
简单 CRUD、单表查询 |
复杂查询、多表关联、动态条件查询 |
三、动态 SQL 编写:MyBatis 的核心优势
动态 SQL 是 MyBatis 的灵魂功能。它允许根据运行时参数动态地拼接 SQL 语句,彻底避免了在 Java 代码中进行繁琐且易错的字符串拼接。虽然两种方式都支持动态 SQL,但 XML 方式的实现更加直观和强大。
1. 核心动态 SQL 标签(XML 方式)
XML 提供了一系列专属标签来优雅地构建动态 SQL。
(1)<if>:条件判断
根据参数是否存在,动态添加 SQL 片段。
<select id="queryUserByCondition" resultType="User">
SELECT id, user_name, email, age FROM t_user
WHERE 1=1
<if test="username != null and username != ''">
AND user_name LIKE CONCAT('%', #{username}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
</select>
(2)<where>:智能条件拼接
用来替代 WHERE 1=1 的写法,能智能地去除条件片段开头多余的 AND 或 OR,避免 SQL 语法错误。
<select id="queryUserByCondition" resultType="User">
SELECT id, user_name, email, age FROM t_user
<where>
<if test="username != null and username != ''">
user_name LIKE CONCAT('%', #{username}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
(3)<foreach>:循环遍历
用于 IN 查询或批量操作,遍历集合或数组参数。
<!-- 根据ID集合批量删除用户 -->
<delete id="batchDeleteUser">
DELETE FROM t_user WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
collection:方法中传入的集合或数组参数名。
item:遍历过程中每个元素的别名。
open/close:循环包装的开始和结束字符串。
separator:各元素之间的分隔符。
(4)<choose> + <when> + <otherwise>:分支选择
类似于 Java 中的 switch-case 或 if-else if-else 结构,只执行第一个满足条件的分支,适用于多条件互斥的场景。
<select id="queryUserByGrade" resultType="User">
SELECT id, user_name, email, age FROM t_user
<where>
<choose>
<when test="minAge != null and maxAge != null">
age BETWEEN #{minAge} AND #{maxAge}
</when>
<when test="minAge != null">
age >= #{minAge}
</when>
<when test="maxAge != null">
age <= #{maxAge}
</when>
<otherwise>
age > 18 <!-- 默认条件 -->
</otherwise>
</choose>
</where>
</select>
2. 注解方式的动态 SQL
注解方式需要通过 @SelectProvider、@InsertProvider 等 “Provider 注解” 来实现动态 SQL。它要求开发者编写一个额外的 Java 类,在其中用字符串拼接的方式构建 SQL。这种方式可读性差,且容易出错,仅建议在动态逻辑非常简单时使用。
// 1. 在Mapper接口中使用Provider注解
public interface UserMapper {
@SelectProvider(type = UserSqlProvider.class, method = "queryUserByConditionSql")
List<User> queryUserByCondition(@Param("username") String username, @Param("age") Integer age);
}
// 2. 编写SQL Provider类
public class UserSqlProvider {
public String queryUserByConditionSql(@Param("username") String username, @Param("age") Integer age) {
StringBuilder sql = new StringBuilder("SELECT id, user_name, email, age FROM t_user WHERE 1=1");
if (username != null && !username.isEmpty()) {
sql.append(" AND user_name LIKE CONCAT('%', #{username}, '%')");
}
if (age != null) {
sql.append(" AND age = #{age}");
}
return sql.toString();
}
}
四、核心总结
Spring Boot 与 MyBatis 的整合,核心在于利用自动配置简化环境搭建。而开发方式的选择,则应基于实际业务场景的复杂度进行权衡:
- 环境搭建三板斧:引入
mybatis-spring-boot-starter 依赖、配置数据库及 MyBatis 参数、使用 @MapperScan 扫描接口。
- 开发方式选择:追求开发效率的简单场景可选注解方式;注重灵活性、可维护性的复杂业务,务必选择 XML 配置方式。
- 动态 SQL:这是 MyBatis 的立身之本,XML 方式通过
<if>, <where>, <foreach> 等标签提供了极其优雅的实现,是处理复杂查询条件的利器。
掌握这两种模式及其适用场景,你就能在 Java 持久层开发中游刃有余。MyBatis 的精髓在于将 SQL 的控制权交还给开发者,在保证性能优化的同时,又不失框架的便利性,这正是它区别于全自动 ORM(如 JPA)的核心价值所在。希望这篇实战指南能帮助你更好地理解和运用 MyBatis。如果你想深入探讨更多架构设计与技术细节,欢迎前往 云栈社区 的 技术文档 板块,与更多开发者交流学习。