MyBatis 注解开发全指南:从基础到高级用法 MyBatis 注解方式提供了一种无需 XML 配置即可编写映射语句的方案,适用于 SQL 逻辑简单、追求代码集中管理的场景。系统梳理注解开发的完整用法,包括基础 CRUD、高级映射、动态 SQL 及最佳实践,帮助你灵活选择 XML 或注解方式。
基础注解:CRUD 操作 MyBatis 提供了 @Insert
、@Update
、@Delete
、@Select
四个核心注解,分别对应 XML 中的 <insert>
、<update>
、<delete>
、<select>
标签,使用简单直接。
1. @Insert
:插入数据 1 2 3 4 5 6 7 8 9 10 public interface UserMapper { @Insert("INSERT INTO user(username, age, email) VALUES(#{username}, #{age}, #{email})") int insertUser (User user) ; @Insert("INSERT INTO user(username, age) VALUES(#{username}, #{age})") @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") int insertUserWithId (User user) ; }
@Options
用于配置插入策略,useGeneratedKeys=true
启用自增主键,keyProperty
绑定实体类属性,keyColumn
绑定数据库列(可选,默认与属性名一致)。
2. @Update
与 @Delete
:更新与删除 1 2 3 4 5 6 7 8 9 public interface UserMapper { @Update("UPDATE user SET username=#{username}, age=#{age} WHERE id=#{id}") int updateUser (User user) ; @Delete("DELETE FROM user WHERE id=#{id}") int deleteUserById (Integer id) ; }
3. @Select
:查询数据
1 2 3 4 5 6 7 8 9 10 11 12 13 public interface UserMapper { @Select("SELECT id, username, age, email FROM user WHERE id=#{id}") User selectById (Integer id) ; @Select("SELECT id, username, age, email FROM user") List<User> selectAll () ; @Select("SELECT * FROM user WHERE username LIKE #{name} AND age > #{minAge}") List<User> selectByCondition (@Param("name") String username, @Param("minAge") Integer age) ; }
多参数时必须使用 @Param
注解指定参数名,否则 MyBatis 无法映射 #{name}
等占位符。
结果映射注解:处理字段映射 当数据库列名与实体类属性名不一致时,需通过结果映射注解自定义映射关系,核心注解为 @Results
、@Result
、@ResultMap
。
1. @Results
与 @Result
:定义映射关系 等价于 XML 中的 <resultMap>
和 <result>
标签:
1 2 3 4 5 6 7 8 9 10 11 12 public interface UserMapper { @Select("SELECT id, user_name, user_age, create_time FROM user WHERE id=#{id}") @Results({ @Result(id = true, column = "id", property = "id"), // id=true 表示为主键 @Result(column = "user_name", property = "username"), // 数据库列 -> 实体属性 @Result(column = "user_age", property = "age"), @Result(column = "create_time", property = "createTime", javaType = LocalDateTime.class, // 指定 Java 类型(可选) jdbcType = JdbcType.TIMESTAMP) // 指定 JDBC 类型(可选) }) User selectByIdWithMapping (Integer id) ; }
column
:数据库表列名;
property
:实体类属性名;
javaType
/jdbcType
:解决类型转换问题(如日期类型)。
2. @ResultMap
:复用结果映射 当多个方法需要相同的结果映射时,可通过 @ResultMap
复用,避免重复代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface UserMapper { @Results(id = "userResultMap", value = { @Result(id = true, column = "id", property = "id"), @Result(column = "user_name", property = "username"), @Result(column = "user_age", property = "age") }) @Select("SELECT id, user_name, user_age FROM user WHERE id=#{id}") User selectById (Integer id) ; @Select("SELECT id, user_name, user_age FROM user WHERE age > #{age}") @ResultMap("userResultMap") List<User> selectByAge (Integer age) ; }
@Results
的 id
属性为映射命名,@ResultMap
通过该 id 引用。
3. 混合 XML 映射:@ResultMap
引用 XML 中的 <resultMap>
当结果映射复杂(如级联查询)时,可在 XML 中定义 <resultMap>
,再通过注解引用:
1 2 3 4 5 6 7 8 <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 >
1 2 3 4 5 6 public interface UserMapper { @Select("SELECT id, user_name, user_age FROM user WHERE id=#{id}") @ResultMap("com.example.mapper.UserMapper.xmlResultMap") User selectByIdWithXmlMap (Integer id) ; }
级联查询注解:处理关联关系 MyBatis 注解提供 @One
(一对一)和 @Many
(一对多)注解,处理实体间的关联关系,等价于 XML 中的 <association>
和 <collection>
。
1. @One
:一对一关联 例如,User
关联 UserInfo
(一个用户对应一个详细信息):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface UserMapper { @Select("SELECT * FROM user WHERE id=#{id}") @Results({ @Result(id = true, column = "id", property = "id"), @Result(column = "username", property = "username"), // 一对一关联:通过 user_id 查询 UserInfo @Result(column = "id", property = "userInfo", one = @One( select = "com.example.mapper.UserInfoMapper.selectByUserId", // 关联查询的 Mapper 方法 fetchType = FetchType.EAGER // 加载策略:立即加载(默认)/延迟加载 ) ) }) User selectUserWithInfo (Integer id) ; } public interface UserInfoMapper { @Select("SELECT * FROM user_info WHERE user_id=#{userId}") UserInfo selectByUserId (Integer userId) ; }
column = "id"
:将 user
表的 id
作为参数传递给 selectByUserId
方法;
fetchType = FetchType.LAZY
:开启延迟加载(需全局配置 lazyLoadingEnabled=true
)。
2. @Many
:一对多关联 例如,User
关联 Order
(一个用户对应多个订单):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface UserMapper { @Select("SELECT * FROM user WHERE id=#{id}") @Results({ @Result(id = true, column = "id", property = "id"), @Result(column = "username", property = "username"), // 一对多关联:通过 user_id 查询订单列表 @Result(column = "id", property = "orders", many = @Many( select = "com.example.mapper.OrderMapper.selectByUserId", fetchType = FetchType.LAZY ) ) }) User selectUserWithOrders (Integer id) ; } public interface OrderMapper { @Select("SELECT * FROM orders WHERE user_id=#{userId}") 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 的二级缓存 1 2 3 4 5 6 7 8 9 10 @CacheNamespace( eviction = LruCache.class, // 缓存回收策略(LRU 最近最少使用) flushInterval = 60000, // 缓存刷新间隔(60秒) size = 1024, // 最大缓存条目 readWrite = true // 是否可读写(false 为只读) ) public interface UserMapper { }
需确保实体类实现 Serializable
接口(readWrite=true
时需要序列化)。
2. @CacheNamespaceRef
:引用其他 Mapper 的缓存 当多个 Mapper 操作同一张表时,可通过该注解共享缓存:
1 2 3 4 5 @CacheNamespaceRef(UserMapper.class) public interface OrderMapper { }
动态 SQL 注解:@*Provider
系列注解 对于复杂的动态 SQL(如条件判断、循环),注解方式通过 @SelectProvider
、@InsertProvider
、@UpdateProvider
、@DeleteProvider
实现,原理是通过工具类动态生成 SQL 语句。
1. 基础用法:静态 SQL 生成 1 2 3 4 5 6 7 8 9 10 11 12 13 public interface UserMapper { @SelectProvider(type = UserProvider.class, method = "selectByIdSql") User selectById (Integer id) ; } public class UserProvider { public String selectByIdSql (Integer id) { return "SELECT id, username, age FROM user WHERE id = " + id; } }
2. 动态 SQL 生成:条件拼接 使用 MyBatis 提供的 SQL
类(构建器)简化 SQL 拼接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public interface UserMapper { @SelectProvider(type = UserProvider.class, method = "selectByCondition") List<User> selectByCondition ( @Param("username") String username, @Param("minAge") Integer minAge, @Param("maxAge") Integer maxAge ) ;} public class UserProvider { public String selectByCondition (Map<String, Object> params) { return new SQL() {{ SELECT("id, username, age" ); FROM("user" ); if (params.get("username" ) != null ) { WHERE("username LIKE CONCAT('%', #{username}, '%')" ); } if (params.get("minAge" ) != null ) { WHERE("age >= #{minAge}" ); } if (params.get("maxAge" ) != null ) { WHERE("age <= #{maxAge}" ); } }}.toString(); } }
SQL
构建器的方法(SELECT
、FROM
、WHERE
)支持链式调用,自动处理空格和关键字拼接;
多参数时,工具类方法参数建议使用 Map<String, Object>
,通过 params.get("key")
获取参数。
3. 批量操作:动态生成批量插入 SQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public interface UserMapper { @InsertProvider(type = UserProvider.class, method = "batchInsert") int batchInsert (@Param("users") List<User> users) ; } public class UserProvider { public String batchInsert (Map<String, Object> params) { List<User> users = (List<User>) params.get("users" ); SQL sql = new SQL() {{ INSERT_INTO("user" ); VALUES("username, age" , "#{user.username}, #{user.age}" ); }}; 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 方式仍具有不可替代的优势
v1.3.10