news 2026/8/26 16:48:23

MyBatis 的注解式开发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MyBatis 的注解式开发

MyBatis 的注解式开发是除了 XML 映射之外的另一种主流方式。它把 SQL 直接写在 Java 接口的方法上,省去了 XML 文件,适合简单查询和快速开发。

下面我从最基础的注解开始,逐步深入到复杂的映射和动态 SQL


一、为什么用注解式开发?

维度注解式XML 式
代码密度高(SQL 和 Java 在同一文件)低(需要单独维护 XML)
可读性简单查询清晰复杂查询更易读
动态 SQL较复杂(用@SelectProvider非常灵活(<if><where>
维护成本低(修改接口即改 SQL)中等(需同时维护接口和 XML)
适用场景简单 CRUD、快速开发复杂联表查询、动态条件多

结论:没有绝对的优劣,实际项目中通常是注解和 XML 混合使用,简单查询用注解,复杂查询用 XML。


二、基础 CRUD 注解

1.@Select/@Insert/@Update/@Delete

publicinterfaceUserMapper{// 查询单条@Select("SELECT * FROM user WHERE id = #{id}")UserselectById(Longid);// 查询列表@Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')")List<User>selectByName(Stringname);// 插入,使用 @Options 返回自增主键@Insert("INSERT INTO user(name, age, email) VALUES(#{name}, #{age}, #{email})")@Options(useGeneratedKeys=true,keyProperty="id")intinsert(Useruser);// 更新@Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")intupdate(Useruser);// 删除@Delete("DELETE FROM user WHERE id = #{id}")intdeleteById(Longid);}

2. 返回自增主键

使用@Options(useGeneratedKeys = true, keyProperty = "id"),插入后 MyBatis 会自动把生成的主键设置到对象的id属性中。

Useruser=newUser();user.setName("张三");userMapper.insert(user);System.out.println("新插入的ID:"+user.getId());// 自动回填

三、参数传递

1. 单个参数(基本类型)

直接用#{参数名}即可。

@Select("SELECT * FROM user WHERE id = #{id}")UserselectById(Longid);

2. 多个参数——使用@Param

多个参数时,必须用@Param注解给参数起别名。

@Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%') AND age > #{age}")List<User>selectByNameAndAge(@Param("name")Stringname,@Param("age")Integerage);

如果没有@Param,MyBatis 会用arg0arg1或者param1param2作为参数名,非常不直观。

3. 传入 POJO 对象

直接用#{属性名}即可,MyBatis 会自动读取对象的 getter 方法。

@Insert("INSERT INTO user(name, age, email) VALUES(#{name}, #{age}, #{email})")intinsert(Useruser);

4. 传入 Map

@Select("SELECT * FROM user WHERE name = #{name} AND age = #{age}")List<User>selectByMap(Map<String,Object>params);

调用时:

Map<String,Object>map=newHashMap<>();map.put("name","张三");map.put("age",25);userMapper.selectByMap(map);

四、结果映射——@Results@Result

当数据库字段名和 Java 属性名不一致时,需要手动映射。

@Select("SELECT id, user_name, user_age FROM user WHERE id = #{id}")@Results(id="userResultMap",value={@Result(column="id",property="id"),@Result(column="user_name",property="name"),@Result(column="user_age",property="age")})UserselectById(Longid);

@Resultsid属性可以给这个映射起一个名字,在其他方法中复用。

@Select("SELECT id, user_name, user_age FROM user")@ResultMap("userResultMap")// 复用上面的映射List<User>selectAll();

五、复杂关联查询——联表映射

1. 一对一关联(@One

publicclassUser{privateLongid;privateStringname;privateDeptdept;// 所属部门}// 查询用户时,顺便查询部门信息@Select("SELECT id, name, dept_id FROM user WHERE id = #{id}")@Results({@Result(column="id",property="id"),@Result(column="name",property="name"),@Result(column="dept_id",property="dept",one=@One(select="com.example.mapper.DeptMapper.selectById"))})UserselectUserWithDept(Longid);

2. 一对多关联(@Many

publicclassDept{privateLongid;privateStringname;privateList<User>users;// 部门下的所有用户}@Select("SELECT * FROM dept WHERE id = #{id}")@Results({@Result(column="id",property="id"),@Result(column="name",property="name"),@Result(column="id",property="users",many=@Many(select="com.example.mapper.UserMapper.selectByDeptId"))})DeptselectDeptWithUsers(Longid);

六、动态 SQL——@SelectProvider

注解方式处理动态 SQL(<if><where>等)比较麻烦,需要用@SelectProvider@InsertProvider等注解。

publicclassUserSqlProvider{publicStringselectByCondition(Map<String,Object>params){Stringname=(String)params.get("name");Integerage=(Integer)params.get("age");SQLsql=newSQL(){{SELECT("*");FROM("user");if(name!=null&&!name.isEmpty()){WHERE("name LIKE CONCAT('%', #{name}, '%')");}if(age!=null){WHERE("age > #{age}");}ORDER_BY("id DESC");}};returnsql.toString();}}// Mapper 中使用@SelectProvider(type=UserSqlProvider.class,method="selectByCondition")List<User>selectByCondition(Map<String,Object>params);

注意@SelectProvidermethod方法返回值必须是 String(即 SQL 语句)。


七、常用注解汇总

注解用途
@Select查询
@Insert插入
@Update更新
@Delete删除
@Options配置选项(如自增主键返回)
@Param参数命名
@Results/@Result结果字段映射
@One一对一关联查询
@Many一对多关联查询
@ResultMap复用已定义的@Results
@SelectProvider动态 SQL 提供者
@InsertProvider/@UpdateProvider/@DeleteProvider同上

八、注解 vs XML:如何选择?

场景推荐方式
单表 CRUD注解
简单联表查询(2-3张表)注解
复杂动态 SQL(多个<if><foreach>XML
5张表以上的复杂联表XML
需要复用 SQL 片段(<sql>XML
快速原型开发注解
大型项目、长期维护混合(注解+XML)

九、混合使用的配置方式

如果同时使用注解和 XML,MyBatis 会优先使用 XML(如果接口方法和 XML 中的 id 相同,XML 会覆盖注解)。更好的做法是明确区分:

  • 简单查询:注解写在接口方法上
  • 复杂查询:XML 写在对应的 Mapper XML 文件中,接口中只声明方法,不加注解
// 接口中不写 @Select,完全由 XML 接管publicinterfaceUserMapper{List<User>selectComplex(Map<String,Object>params);}
<!-- XML 文件中定义复杂 SQL --><selectid="selectComplex"resultType="User">SELECT * FROM user<where><iftest="name != null">name LIKE CONCAT('%', #{name}, '%')</if><!-- 更多复杂条件 --></where></select>

十、最佳实践建议

  1. 简单 CRUD 用注解,复杂查询用 XML,不要把注解搞得过于复杂。
  2. @Results一定要复用,通过id@ResultMap避免重复定义。
  3. 动态 SQL 超过 5 行时,果断用 XML,可读性更好。
  4. @Param不要省,哪怕只有一个参数也养成习惯,方便后期扩展。
  5. @SelectProvider适合复杂动态 SQL,但 SQL 构造逻辑建议单独放在一个类里,保持 Mapper 接口的干净。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 16:48:09

【TDengine】 TDengine 的内存管理模型是怎样的?缓存机制如何工作?

TDengine 3.4.x 内存管理与缓存机制深度解析:从内存池到查询加速器 问题原文:“TDengine 的内存管理模型是怎样的?缓存机制如何工作?” 解析范围:本文将深入 TDengine 3.4.x 社区版的内存管理体系,系统性地剖析其自研内存池(Memory Pool)、多级缓存架构(Last/Block Ca…

作者头像 李华
网站建设 2026/8/26 16:48:03

【TDengine】TDengine 使用了哪些压缩算法?压缩率通常能达到多少?

TDengine 3.4.x 压缩算法全景图:从 Gorilla 到 LZ4 的极致存储优化 问题原文:“TDengine 使用了哪些压缩算法?压缩率通常能达到多少?” 解析范围:本文将系统性地拆解 TDengine 3.4.x 社区版所采用的多层、类型感知的压缩策略。我们将深入其核心编码算法(Delta-of-Delta, …

作者头像 李华
网站建设 2026/8/26 16:44:26

Video2X 完整上手:三步把模糊老视频放大到 4K、30fps 变 60fps

Video2X 完整上手&#xff1a;三步把模糊老视频放大到 4K、30fps 变 60fps 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/…

作者头像 李华