使用注解方式
MyBatis 注解开发全指南:从基础到高级用法
MyBatis 注解方式提供了一种无需 XML 配置即可编写映射语句的方案,适用于 SQL 逻辑简单、追求代码集中管理的场景。系统梳理注解开发的完整用法,包括基础 CRUD、高级映射、动态 SQL 及最佳实践,帮助你灵活选择 XML 或注解方式。
基础注解:CRUD 操作
MyBatis 提供了 @Insert、@Update、@Delete、@Select 四个核心注解,分别对应 XML 中的 <insert>、<update>、<delete>、<select> 标签,使用简单直接。
1. @Insert:插入数据
public interface UserMapper {
// 基础插入
int insertUser(User user);
// 插入并返回自增主键(等价于 XML 中的 useGeneratedKeys)
int insertUserWithId(User user);
}
@Options用于配置插入策略,useGeneratedKeys=true启用自增主键,keyProperty绑定实体类属性,keyColumn绑定数据库列(可选,默认与属性名一致)。
2. @Update 与 @Delete:更新与删除
public interface UserMapper {
// 更新用户
int updateUser(User user);
// 根据 ID 删除
int deleteUserById(Integer id);
}
3. @Select:查询数据
public interface UserMapper {
// 根据 ID 查询单个用户
User selectById(Integer id);
// 查询所有用户
List<User> selectAll();
// 多参数查询(需用 @Param 绑定参数名)
List<User> selectByCondition( String username, Integer age);
}
- 多参数时必须使用
@Param注解指定参数名,否则 MyBatis 无法映射#{name}等占位符。
结果映射注解:处理字段映射
当数据库列名与实体类属性名不一致时,需通过结果映射注解自定义映射关系,核心注解为 @Results、@Result、@ResultMap。
1. @Results 与 @Result:定义映射关系
等价于 XML 中的 <resultMap> 和 <result> 标签:
public interface UserMapper {
User selectByIdWithMapping(Integer id);
}
column:数据库表列名;property:实体类属性名;javaType/jdbcType:解决类型转换问题(如日期类型)。
2. @ResultMap:复用结果映射
当多个方法需要相同的结果映射时,可通过 @ResultMap 复用,避免重复代码:
public interface UserMapper {
// 定义基础结果映射
User selectById(Integer id);
// 复用结果映射
// 引用上面定义的 id="userResultMap" 的映射
List<User> selectByAge(Integer age);
}
@Results的id属性为映射命名,@ResultMap通过该 id 引用。
3. 混合 XML 映射:@ResultMap 引用 XML 中的 <resultMap>
当结果映射复杂(如级联查询)时,可在 XML 中定义 <resultMap>,再通过注解引用:
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="xmlResultMap" type="com.example.model.User">
<id column="id" property="id"/>
<result column="user_name" property="username"/>
<result column="user_age" property="age"/>
</resultMap>
</mapper>
// UserMapper.java
public interface UserMapper {
// 全限定名引用 XML 中的 resultMap
User selectByIdWithXmlMap(Integer id);
}
级联查询注解:处理关联关系
MyBatis 注解提供 @One(一对一)和 @Many(一对多)注解,处理实体间的关联关系,等价于 XML 中的 <association> 和 <collection>。
1. @One:一对一关联
例如,User 关联 UserInfo(一个用户对应一个详细信息):
public interface UserMapper {
User selectUserWithInfo(Integer id);
}
// UserInfoMapper.java
public interface UserInfoMapper {
UserInfo selectByUserId(Integer userId);
}
column = "id":将user表的id作为参数传递给selectByUserId方法;fetchType = FetchType.LAZY:开启延迟加载(需全局配置lazyLoadingEnabled=true)。
2. @Many:一对多关联
例如,User 关联 Order(一个用户对应多个订单):
public interface UserMapper {
User selectUserWithOrders(Integer id);
}
// OrderMapper.java
public interface OrderMapper {
List<Order> selectByUserId(Integer userId);
}
级联查询注意事项
- N+1 查询问题:注解方式的级联查询默认会触发 N+1 次 SQL(1 次主查询 + N 次关联查询),性能较差;
- 优化方案:复杂关联查询建议使用 XML 中的
join语句 + 结果映射,或通过@SelectProvider编写动态 SQL 实现关联查询。
缓存注解:二级缓存配置
MyBatis 注解提供 @CacheNamespace 和 @CacheNamespaceRef 注解,对应 XML 中的 <cache> 和 <cache-ref>,用于配置二级缓存。
1. @CacheNamespace:开启当前 Mapper 的二级缓存
// 在 Mapper 接口上添加缓存注解
public interface UserMapper {
// ... 方法定义
}
- 需确保实体类实现
Serializable接口(readWrite=true时需要序列化)。
2. @CacheNamespaceRef:引用其他 Mapper 的缓存
当多个 Mapper 操作同一张表时,可通过该注解共享缓存:
// OrderMapper 引用 UserMapper 的缓存配置
public interface OrderMapper {
// ... 方法定义
}
动态 SQL 注解:@*Provider 系列注解
对于复杂的动态 SQL(如条件判断、循环),注解方式通过 @SelectProvider、@InsertProvider、@UpdateProvider、@DeleteProvider 实现,原理是通过工具类动态生成 SQL 语句。
1. 基础用法:静态 SQL 生成
public interface UserMapper {
// 引用 UserProvider 类的 selectById 方法生成 SQL
User selectById(Integer id);
}
// SQL 生成工具类
public class UserProvider {
// 方法返回值为 SQL 字符串
public String selectByIdSql(Integer id) {
return "SELECT id, username, age FROM user WHERE id = " + id;
}
}
2. 动态 SQL 生成:条件拼接
使用 MyBatis 提供的 SQL 类(构建器)简化 SQL 拼接:
public interface UserMapper {
// 动态查询:根据条件拼接 SQL
List<User> selectByCondition(
String username,
Integer minAge,
Integer maxAge
);
}
public class UserProvider {
public String selectByCondition(Map<String, Object> params) {
// 使用 SQL 构建器
return new SQL() {{
SELECT("id, username, age");
FROM("user");
// 条件判断:若 username 不为空,添加 LIKE 条件
if (params.get("username") != null) {
WHERE("username LIKE CONCAT('%', #{username}, '%')");
}
// 若 minAge 不为空,添加 >= 条件
if (params.get("minAge") != null) {
WHERE("age >= #{minAge}");
}
// 若 maxAge 不为空,添加 <= 条件
if (params.get("maxAge") != null) {
WHERE("age <= #{maxAge}");
}
}}.toString();
}
}
SQL构建器的方法(SELECT、FROM、WHERE)支持链式调用,自动处理空格和关键字拼接;- 多参数时,工具类方法参数建议使用
Map<String, Object>,通过params.get("key")获取参数。
3. 批量操作:动态生成批量插入 SQL
public interface UserMapper {
// 批量插入
int batchInsert( List<User> users);
}
public class UserProvider {
public String batchInsert(Map<String, Object> params) {
List<User> users = (List<User>) params.get("users");
// 构建批量插入 SQL
SQL sql = new SQL() {{
INSERT_INTO("user");
VALUES("username, age", "#{user.username}, #{user.age}");
}};
// 拼接多个 VALUES 子句
String baseSql = sql.toString();
StringBuilder sb = new StringBuilder(baseSql);
for (int i = 1; i < users.size(); i++) {
sb.append(", (#{users[").append(i).append("].username}, #{users[").append(i).append("].age})");
}
return sb.toString();
}
}
注解 vs XML:如何选择?
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 简单 CRUD 操作 | 注解方式 | 代码集中,无需切换文件,开发效率高 |
| 复杂动态 SQL | XML 方式 | XML 标签(if、foreach 等)更直观,维护性好 |
| 复杂结果映射(级联) | XML 方式 | XML 的 <resultMap> 支持更丰富的映射配置,避免注解代码冗长 |
| 多数据库适配 | XML 方式 | 支持 <databaseIdProvider> 标签,注解方式需通过 Provider 类硬编码处理 |
| 团队协作 | 统一方式 | 避免混合使用导致风格混乱,大型项目建议以 XML 为主(可读性强) |
最佳实践
- 避免过度使用注解:复杂 SQL 用 XML,简单 SQL 用注解,平衡开发效率与可维护性;
- 参数绑定规范:多参数必须用
@Param注解,明确参数名,避免arg0、param1等模糊命名; - 动态 SQL 工具类:使用
SQL构建器生成动态 SQL,避免字符串拼接错误; - 级联查询优化:注解方式的级联查询(
@One/@Many)易导致 N+1 问题,优先使用JOIN语句; - 缓存配置:二级缓存建议在 XML 中配置(更直观),注解方式适合简单场景。
总结
MyBatis 注解方式为简单 SQL 操作提供了便捷的解决方案,通过 @Select、@Insert 等基础注解可快速实现 CRUD,配合 @Results、@One、@Many 可处理结果映射和关联查询。但对于复杂动态 SQL 和级联关系,XML 方式仍具有不可替代的优势