我们在使用 mybatis的时候,多数情况下都是创建一个对应 mapper 的 xml 文件来写 sql 语句,这种方式也是官方推荐并且是最灵活的方式。但其实 mybatis 也支持通过注解的方式来实现 sql 语句。本篇文章就是聊聊 mybatis 是如何使用注解方式来实现 sql 执行的。
知识点
- 什么是注解
- 如何使用
- 实现原理
什么是注解
翻译过来可能很多人不清楚,翻译回去就是 Annotation。我们平时在代码中一定用到很多注解,特别是 springboot 项目,看下面的例子你就明白了
@SpringBootApplication
public class MybatisAnalyzeApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisAnalyzeApplication.class, args);
}
}
这是非常简单的一个 springboot 启动类,这个@SpringBootApplication
就是注解。
如何使用
mybatis 注解方式的使用非常简单,直接在你定义的 Mapper 接口对应的方法上加一个注解就可以了,直接上代码:
@Repository
public interface UserInfoMapper {
UserInfo selectById(int id);
@Select("select * from user_info where user_name = #{userName}")
List<UserInfo> select(String userName, String nickName);
int insert(UserInfo userInfo);
int update(int id, String nickName);
}
可以看到UserInfoMapper
接口中的select
方法上面有一个@Select
的注解,这样就完了。其他几个没有加注解的方法,还是需要定义 xml 文件(注解方式和 xml 方式是可以混用的),需要注意的是 xml 文件中不能再定义已经加过注解的方法,否走会抛出异常。同样的,对于增、改、删操作,加上注解@Insert
、@Update
、@Delete
即可。
如果我们还需要用到一些启用缓存、等待超时时间等属性怎么办呢,这些可是在原来的 xml 配置中可以配置的。别急,可以再加一个@Options
注解,如下
@Options(useCache = true, timeout = 3000)
@Select("select * from user_info where user_name = #{userName}")
List<UserInfo> select(String userName, String nickName);
当然还有@SelectKey
、@ResultMap
等注解是和原来的 xml 方式配置对应起来的,不过这里是有局限性的,比如@ResultMap
就需要结合 xml 配置来使用,这也是为什么官方推荐 xml 方式的原因之一。
实现原理
最后我们来了解一下 mybatis 是如何实现注解方式来执行 sql 的,顺带的介绍一下为什么我们定义的 mapper 明明是接口类型,为什么能够在代码中直接使用。先来看一下注解所在包
上面这个包里包含了所有注解类型,基本上和 xml 配置的属性是可以关联起来的,如果不知道 xml 方式对应的注解方式怎么使用,可以直接到这里看看。接着看下哪里启动时加载 mapper 的地方`org.apache.ibatis.binding.MapperRegistry#addMapper
在这个逻辑中,我们可以看到有一个knownMappers
,它是存放所有我们定义的 mapper 接口的,对应的 value 类型是MapperProxyFactory
,这个类型很重要,后面再讲。可以看到这个类型MapperAnnotationBuilder
,看名称就知道是专门对注解方式进行处理的类,与之对应的处理 xml 的类就是XMLMapperBuilder
。跟进去看下parser.parse()
逻辑
这里优先会去解析 xml 配置,接着逐个解析 mapper 的方法,跟进去看下如何解析的
可以看到这里就对注解进行解析了,方法上的注解一定要在以下范围内才能解析
后面每个注解的解析就不介绍了,比较简单。
集成 spring
为什么我们定义的 mapper 明明是接口类型,为什么能够在代码中直接使用?这是因为集成了 spring,由 mybatis-spring 包帮我们做掉了。我们平时在 spring 中使用的还是需要指定扫描的 mapper 包所在路径,注解方式就是@MapperScan
它会通过MapperScannerRegistrar
将 mapper 注册为 bean,最终在org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions
设置实际的 mapper 类型(MapperFactoryBean)
而MapperFactoryBean
是一个FactoryBean
,所以实际的 bean 是通过它的getObject
方法获取到的
跟进去最终在这里获取`org.apache.ibatis.binding.MapperRegistry#getMapper
其实就是一个动态代理。
总结
注解这一块实现还是比较简单的,对于集成 spring 并做动态代理的方式,我们自己的代码里也可以参考。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。