Java——MyBatis 核心特性全解析:从配置到高级用法
MyBatis 核心特性全解析:从配置到高级用法
MyBatis 作为一款灵活高效的持久层框架,其核心特性涵盖配置管理、参数处理、查询优化等多个方面。本文基于官方文档,从核心配置、参数处理、高级查询到缓存策略,全面解析 MyBatis 的核心用法,帮助开发者深入理解并灵活运用框架特性。
一、核心配置文件详解
MyBatis 的核心配置文件(如 SqlMapConfig.xml
)是框架运行的基础,包含了数据源、别名、环境等关键配置,以下是核心标签的详细说明:
1. <properties>
:属性配置
用于引入外部配置文件或定义内部属性,实现动态配置数据源信息,提高配置灵活性。
-
引入外部文件:
<properties resource="db.properties"/> <!-- 引用resources目录下的db.properties -->
外部文件
db.properties
内容:jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis jdbc.username=root jdbc.password=root
-
内部定义属性:
<properties><property name="jdbc.driver" value="com.mysql.jdbc.Driver"/> </properties>
-
使用属性:通过
${属性名}
引用,例如配置数据源时:<dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/> </dataSource>
2. <typeAliases>
:类型别名
为 Java 类定义简短别名,简化映射文件中类型的引用,减少冗余代码。
-
单个类别名:
<typeAliases><typeAlias type="com.example.pojo.User" alias="User"/> <!-- 为User类定义别名"User" --> </typeAliases>
映射文件中可直接使用别名:
<select id="findAll" resultType="User">select * from user</select>
-
包扫描别名:为指定包下所有类自动生成别名(别名与类名相同,首字母可小写):
<typeAliases><package name="com.example.pojo"/> <!-- 包下所有类自动生成别名 --> </typeAliases>
-
系统默认别名:MyBatis 对常用类型提供默认别名,如
string
对应java.lang.String
,int
对应java.lang.Integer
等。
3. <environments>
:环境配置
用于配置数据库环境(如开发、测试、生产环境),支持多环境切换,通过 default
指定默认环境。
- 事务管理器:
<transactionManager>
配置事务管理方式,type="JDBC"
表示使用 JDBC 自带的事务管理(手动提交/回滚); - 数据源:
<dataSource>
配置数据库连接,type="POOLED"
表示使用 MyBatis 自带的连接池(推荐),UNPOOLED
表示不使用连接池,JNDI
表示由容器管理连接。
示例配置:
<environments default="mysql"><environment id="mysql"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment>
</environments>
4. <mappers>
:映射器配置
用于注册映射文件或 Mapper 接口,只有注册后才能被 MyBatis 识别。支持四种注册方式:
- 相对路径注册映射文件:
<mappers><mapper resource="com/example/mapper/UserMapper.xml"/> </mappers>
- 绝对路径注册映射文件:
<mapper url="file:///D:/project/mapper/UserMapper.xml"/>
- 注册 Mapper 接口(适合注解开发):
<mapper class="com.example.mapper.UserMapper"/>
- 包扫描注册(注册指定包下所有接口):
<package name="com.example.mapper"/>
二、映射文件高级特性
映射文件(如 UserMapper.xml
)是 MyBatis 定义 SQL 语句和映射规则的核心,除了基础的 CRUD 标签,还有多个高级标签用于优化 SQL 编写。
1. <resultMap>
:自定义映射关系
当 POJO 属性名与数据库列名不一致时,通过 <resultMap>
手动建立映射关系,解决自动映射失效问题。
示例:数据库表 teacher
列名为 tid
、tname
,POJO 类 Teacher
属性为 id
、teacherName
:
<resultMap id="teacherMap" type="Teacher"><id property="id" column="tid"/> <!-- 主键映射:POJO属性id对应列tid --><result property="teacherName" column="tname"/> <!-- 普通字段映射:teacherName对应tname -->
</resultMap><select id="findAll" resultMap="teacherMap">select * from teacher <!-- 使用自定义映射 -->
</select>
2. <sql>
与 <include>
:SQL 片段复用
将重复的 SQL 片段(如查询字段、条件)定义在 <sql>
中,通过 <include>
引用,减少代码冗余。
示例:复用查询字段片段:
<sql id="userColumns">id, username, sex, address</sql> <!-- 定义可复用的字段片段 --><select id="findById" resultType="User">select <include refid="userColumns"/> from user where id = #{id} <!-- 引用片段 -->
</select><select id="findAll" resultType="User">select <include refid="userColumns"/> from user <!-- 多次复用 -->
</select>
3. 特殊字符处理
映射文件中直接使用 <
、>
、&
等特殊字符会导致 XML 解析错误,需使用实体符号替代:
符号 | 实体符号 |
---|---|
< | < |
> | > |
& | & |
' | ' |
" | " |
示例:查询年龄大于 18 的用户:
<select id="findAdult" resultType="User">select * from user where age > 18 <!-- 使用>替代> -->
</select>
三、参数处理机制
MyBatis 提供了灵活的参数处理方式,支持单参数、多参数传递,以及动态参数拼接,满足不同场景的需求。
1. #{}
与 ${}
的区别
两者均用于接收参数,但使用场景和安全性不同:
-
#{}
:SQL 占位符,参数会被预编译为?
,自动添加引号,可防止 SQL 注入,推荐优先使用。
示例:<select id="findByName" parameterType="string" resultType="User">select * from user where username like #{name} <!-- 参数自动加引号,如'%张三%' --> </select>
-
${}
:字符串拼接,参数直接嵌入 SQL 语句,无法防止 SQL 注入,适用于动态表名、排序字段等场景。
示例:动态指定排序字段:<select id="findOrder" parameterType="string" resultType="User">select * from user order by ${sortField} <!-- 直接拼接字段名,如"age desc" --> </select>
注意:使用
${}
时,若参数为单一值,参数名必须为value
(如${value}
)。
2. 多参数传递方式
当 Mapper 接口方法包含多个参数时,MyBatis 提供四种传递方式:
(1)顺序传参
通过 arg0、arg1...
或 param1、param2...
引用参数,按参数顺序匹配(可读性差,不推荐)。
示例:
// 接口方法
List<User> findPage(int startIndex, int pageSize);
映射文件:
<select id="findPage" resultType="User">select * from user limit #{arg0}, #{arg1} <!-- arg0对应第一个参数,arg1对应第二个参数 -->
</select>
(2)@Param
注解传参
在参数前通过 @Param
定义名称,映射文件中直接使用该名称引用(推荐,参数直观)。
示例:
// 接口方法
List<User> findPage1(@Param("startIndex") int startIndex, @Param("pageSize") int pageSize);
映射文件:
<select id="findPage1" resultType="User">select * from user limit #{startIndex}, #{pageSize} <!-- 直接使用注解定义的名称 -->
</select>
(3)POJO 传参
将参数封装到 POJO 类中,映射文件通过 POJO 的属性名引用(适合参数较多的场景)。
示例:
// 自定义POJO
public class PageQuery {private int startIndex;private int pageSize;// getter/setter
}// 接口方法
List<User> findPage2(PageQuery pageQuery);
映射文件:
<select id="findPage2" parameterType="PageQuery" resultType="User">select * from user limit #{startIndex}, #{pageSize} <!-- 引用POJO的属性 -->
</select>
(4)Map 传参
使用 Map
存储参数,映射文件通过 Map 的 Key 引用(无需自定义 POJO,灵活度高)。
示例:
// 接口方法
List<User> findPage3(Map<String, Object> params);
映射文件:
<select id="findPage3" parameterType="map" resultType="User">select * from user limit #{startIndex}, #{pageSize} <!-- 引用Map的Key -->
</select>
测试时传入参数:
Map<String, Object> params = new HashMap<>();
params.put("startIndex", 0);
params.put("pageSize", 3);
userMapper.findPage3(params);
四、主键回填与高级查询
1. 主键回填
当插入数据时需要获取自增主键(如 MySQL 的自增 ID),可通过 <selectKey>
标签实现主键回填。
示例:
<insert id="add" parameterType="User"><!-- 主键回填配置 --><selectKey keyProperty="id" <!-- POJO中主键属性名 -->keyColumn="id" <!-- 数据库中主键列名 -->resultType="int" <!-- 主键类型 -->order="AFTER" <!-- 在insert后执行(自增主键需用AFTER) -->>SELECT LAST_INSERT_ID() <!-- 查询刚插入的主键值(MySQL专用) --></selectKey>insert into user(username, sex, address) values(#{username}, #{sex}, #{address})
</insert>
测试时,插入后可直接获取主键:
User user = new User("张三", "男", "北京");
userMapper.add(user);
session.commit();
System.out.println("新增用户ID:" + user.getId()); // 输出自增ID
2. Example 类高级查询(MyBatis Generator)
MyBatis Generator(MBG)可自动生成 Example
类,用于构建复杂查询条件(如多条件组合、排序、分页等),无需手动编写动态 SQL。
(1)Example 类核心组件
Criteria
:生成查询条件的工具类,通过andXXX()
方法添加条件;or()
:将多个条件组合为OR
关系(默认为AND
);setOrderByClause()
:设置排序规则。
(2)使用示例
假设有 Product
表,MBG 生成 ProductExample
类,实现多条件查询:
-
多条件
AND
查询:ProductExample example = new ProductExample(); ProductExample.Criteria criteria = example.createCriteria(); criteria.andProductNameLike("%课程%"); // 商品名包含"课程" criteria.andPriceBetween(0.0, 20000.0); // 价格在0到20000之间 List<Product> products = productMapper.selectByExample(example);
-
多条件
OR
查询:ProductExample example = new ProductExample(); // 条件1:价格<100 ProductExample.Criteria c1 = example.createCriteria(); c1.andPriceLessThan(100.0);// 条件2:商品名包含"免费" ProductExample.Criteria c2 = example.createCriteria(); c2.andProductNameLike("%免费%");example.or(c2); // 组合为OR关系 List<Product> products = productMapper.selectByExample(example);
-
排序查询:
ProductExample example = new ProductExample(); example.setOrderByClause("price desc"); // 按价格降序 List<Product> products = productMapper.selectByExample(example);
五、缓存与加载策略
1. 缓存机制
MyBatis 提供两级缓存,减少数据库交互,提高查询效率:
-
一级缓存:存在于
SqlSession
中,默认开启,同一SqlSession
内的相同查询复用缓存。
失效场景:执行增删改操作、调用clearCache()
/commit()
、关闭SqlSession
。 -
二级缓存:存在于
SqlSessionFactory
中,需手动开启,同一工厂创建的SqlSession
共享缓存。
开启步骤:- 核心配置文件开启缓存开关(默认开启):
<settings><setting name="cacheEnabled" value="true"/> </settings>
- 映射文件中声明缓存:
<mapper namespace="com.example.mapper.UserMapper"><cache size="2048"/> <!-- 开启二级缓存,最多缓存2048个对象 --> </mapper>
- POJO 类实现
Serializable
接口(二级缓存存储对象数据,需序列化)。
- 核心配置文件开启缓存开关(默认开启):
2. 延迟加载(懒加载)
分解式查询(N+1 查询)中,延迟加载可实现“按需查询”,仅在使用关联数据时才执行查询,减少不必要的数据库交互。
-
开启全局延迟加载:
<settings><setting name="lazyLoadingEnabled" value="true"/> <!-- 全局开启延迟加载 --> </settings>
-
局部设置加载方式:在关联查询中通过
fetchType
指定(lazy
延迟加载,eager
立即加载):<!-- 一对多查询:班级->学生,延迟加载学生列表 --> <collection property="studentList" column="cid" ofType="Student" select="com.example.mapper.StudentMapper.findByClassId"fetchType="lazy" />
-
防止
toString()
触发延迟加载:默认情况下,调用toString()
会触发延迟加载,可通过配置禁用:<settings><setting name="lazyLoadTriggerMethods" value=""/> <!-- 清空触发方法 --> </settings>
总结
MyBatis 的核心特性围绕“灵活”与“高效”设计,从配置文件的动态管理到参数处理的多样方式,从高级查询的 Example 类到缓存与加载策略的优化,每一项特性都旨在简化开发并提升性能。掌握这些核心特性,能帮助开发者在实际项目中灵活应对复杂业务场景,写出高效、易维护的持久层代码。