1. MyBatis注解开发入门
如果你正在使用MyBatis框架,但又厌倦了XML配置文件的繁琐,那么注解开发绝对是你的福音。我刚开始接触MyBatis时,也是从XML配置入手的,后来发现注解方式简直不要太方便!特别是对于中小型项目,注解开发能显著提升编码效率。
@Select
和
@Update
是MyBatis中最常用的两个注解,它们可以直接在Mapper接口的方法上编写SQL语句。想象一下,以前我们需要在XML文件中写SQL,然后在接口中定义方法,现在直接在接口方法上写SQL,代码量直接减半!
// 传统XML方式需要额外编写映射文件
public interface UserMapper {
User getUserById(Integer id);
}
// 注解方式直接在接口中完成
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(Integer id);
}
不过要注意,注解方式虽然方便,但在处理复杂SQL时可能会显得力不从心。我在实际项目中发现,当SQL超过5行时,可读性就会明显下降。这时候就需要用到动态SQL标签来优化了。
2. @Select注解深度解析
2.1 基础查询用法
@Select
注解最基本的用法就是直接在注解中写SQL语句。我刚开始用时觉得特别神奇,Java代码里直接写SQL,还能自动映射结果!
@Select("SELECT id, username, email FROM users WHERE id = #{userId}")
User selectUserById(@Param("userId") Integer id);
这里有个小技巧:使用
@Param
注解明确指定参数名,这样在复杂SQL中更不容易出错。我曾经因为没加这个注解调试了半天,参数名对不上导致SQL执行失败。
2.2 结果集映射
当数据库字段名和Java属性名不一致时,我们可以用
@Results
和
@Result
注解来映射:
@Select("SELECT user_id, user_name, user_email FROM users")
@Results({
@Result(property = "id", column = "user_id"),
@Result(property = "username", column = "user_name"),
@Result(property = "email", column = "user_email")
})
List<User> selectAllUsers();
在实际项目中,我建议字段名和属性名尽量保持一致,可以省去这些映射配置。如果必须使用不同命名,可以考虑在MyBatis配置中开启驼峰命名转换:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2.3 动态SQL构建
这才是
@Select
注解真正强大的地方!通过
<script>
标签,我们可以在注解中使用各种动态SQL标签:
@Select("<script>" +
"SELECT * FROM users " +
"<where> " +
" <if test='username != null'>AND username = #{username}</if> " +
" <if test='email != null'>AND email = #{email}</if> " +
"</where> " +
"</script>")
List<User> selectUsersByCondition(@Param("username") String username,
@Param("email") String email);
我第一次看到这种写法时觉得很别扭,SQL被拆分成多个字符串用加号连接。但习惯后发现这种写法其实很灵活,特别是配合IDE的代码格式化功能后,可读性还不错。
3. @Update注解实战技巧
3.1 基础更新操作
@Update
注解的基本用法和
@Select
类似:
@Update("UPDATE users SET username = #{username}, email = #{email} WHERE id = #{id}")
int updateUser(User user);
这里要注意返回值是int类型,表示受影响的行数。在实际业务中,我建议总是检查这个返回值,确保更新操作确实生效了。
3.2 动态更新字段
在更新操作中,我们经常只需要更新部分字段。这时候
<set>
和
<if>
标签就派上用场了:
@Update("<script>" +
"UPDATE users " +
"<set> " +
" <if test='username != null'>username = #{username},</if> " +
" <if test='email != null'>email = #{email},</if> " +
"</set> " +
"WHERE id = #{id} " +
"</script>")
int updateUserSelective(User user);
这里有个坑我踩过:
<set>
标签会自动去掉最后一个逗号,所以即使最后一个
<if>
条件成立,生成的SQL也是正确的。刚开始我还手动处理逗号,结果发现根本不需要。
3.3 批量更新操作
批量更新是实际项目中很常见的需求,我们可以使用
<foreach>
标签实现:
@Update("<script>" +
"UPDATE users " +
"SET status = #{status} " +
"WHERE id IN " +
"<foreach collection='ids' item='id' open='(' separator=',' close=')'> " +
" #{id} " +
"</foreach> " +
"</script>")
int batchUpdateStatus(@Param("ids") List<Integer> ids, @Param("status") String status);
这种写法性能比在Java中循环执行单条更新要好得多。我在一个用户管理系统中使用后,批量操作耗时从秒级降到了毫秒级。
4. 高级应用与性能优化
4.1 多表关联查询
注解方式同样支持多表查询,只是SQL会变得比较长:
@Select("SELECT u.*, a.province, a.city " +
"FROM users u LEFT JOIN addresses a ON u.id = a.user_id " +
"WHERE u.id = #{userId}")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "address.province", column = "province"),
@Result(property = "address.city", column = "city")
})
User selectUserWithAddress(@Param("userId") Integer userId);
对于特别复杂的关联查询,我建议还是使用XML方式,可读性会更好。或者考虑使用
@One
和
@Many
注解实现延迟加载。
4.2 分页查询优化
分页查询在注解中的实现需要注意SQL方言问题:
// MySQL分页
@Select("SELECT * FROM users LIMIT #{offset}, #{pageSize}")
List<User> selectUsersByPage(@Param("offset") int offset, @Param("pageSize") int pageSize);
// Oracle分页(使用ROWNUM)
@Select("<script>" +
"SELECT * FROM (" +
" SELECT a.*, ROWNUM rn FROM (" +
" SELECT * FROM users ORDER BY id" +
" ) a WHERE ROWNUM <= #{end}" +
") WHERE rn > #{start}" +
"</script>")
List<User> selectUsersByPageForOracle(@Param("start") int start, @Param("end") int end);
在实际项目中,我推荐使用PageHelper这类分页插件,它们能自动识别数据库方言并生成合适的分页SQL。
4.3 二级缓存配置
注解方式也可以配置二级缓存,只需要在Mapper接口上添加
@CacheNamespace
注解:
@CacheNamespace(implementation = MyCustomCache.class, eviction = MyCustomCache.class)
public interface UserMapper {
// 方法定义
}
关于缓存,我有过惨痛教训:在电商系统中,商品信息使用了缓存但没设置合适的过期策略,导致价格更新后前端显示的还是旧价格。所以使用缓存一定要谨慎,考虑好数据一致性问题。
5. 最佳实践与常见问题
5.1 注解与XML的选择策略
根据我的经验,以下情况适合使用注解:
- SQL比较简单
- 项目规模较小
- 需要快速开发原型
以下情况建议使用XML:
- SQL非常复杂
- 需要大量动态SQL
- 团队有专门的DBA负责SQL优化
其实两者可以混用,我在实际项目中经常这样做:简单的CRUD用注解,复杂的统计查询用XML。
5.2 SQL注入防护
注解方式同样需要注意SQL注入问题。MyBatis的
#{}
语法会预编译参数,是安全的。但使用
${}
时要特别小心:
// 安全写法
@Select("SELECT * FROM users WHERE username = #{name}")
User findByUsername(@Param("name") String name);
// 危险写法(可能SQL注入)
@Select("SELECT * FROM ${tableName} WHERE username = #{name}")
User findByUsernameDynamicTable(@Param("tableName") String tableName,
@Param("name") String name);
5.3 日志调试技巧
当注解SQL执行出错时,我常用的调试方法是开启MyBatis的完整日志:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
这样可以在控制台看到实际执行的SQL和参数,非常方便排查问题。
5.4 代码组织建议
对于较长的注解SQL,我建议这样组织代码:
- 每个SQL方法保持单一职责
- 复杂SQL拆分成多行,用加号连接
- 使用IDE的代码格式化功能保持缩进
- 添加必要的注释说明SQL用途
@Select("<script>" +
"/* 根据条件查询活跃用户 */ " +
"SELECT u.id, u.username, u.email " +
"FROM users u " +
"<where> " +
" u.status = 'ACTIVE' " +
" <if test='keyword != null'> " +
" AND (u.username LIKE CONCAT('%', #{keyword}, '%') " +
" OR u.email LIKE CONCAT('%', #{keyword}, '%')) " +
" </if> " +
"</where> " +
"ORDER BY u.create_time DESC " +
"</script>")
List<User> selectActiveUsers(@Param("keyword") String keyword);
这种写法虽然SQL被拆分成多个字符串,但通过良好的格式化和注释,可读性依然很高。
300



被折叠的 条评论
为什么被折叠?



