mybatis开发,你用 xml 还是注解?我 pick ...

1,201 阅读4分钟

两种形式,三种写法

最近在看公司的一些项目的时候发现有的项目里面的 mybatis 是基于注解开发的。而我个人的习惯是基于 xml 文件开发。

所以对于基于注解开发的原理不太了解,于是去翻看了一下相关源码,形成此文。

本文主要介绍基于 mybatis 开发的两种形式,三种写法。

其中两种形式是指:

  1. 基于 xml 文件。
  2. 基于注解开发。

三种写法是指除了 xml 的形式外,注解又有两种不同的写法,它们的实现原理也略有不同,拿 Select 语句举例,就有两种注解 @Select、@SelectProvider 。

演示示例

先上一个演示示例给大家直观的感受一下:

首先,我们有个用户表,包含这些字段和这样一条数据:

image

然后我们搞个接口类,用三种方式去查询用户的年龄,具体如下:

image

xmlQueryAgeByName 方法是使用 xml 的方法去查询用户年龄,对应的 xml 如下:

image

annotationQueryAgeByName 方法是使用 @Select 注解去查询用户的年龄,SQL 就写在注解里面:

image

classQueryAgeByName 方法是使用 @SelectProvider 注解去查询用户的年龄,可以看到注解里面有个 type 字段,对应一个 class 类。一个 method 字段,对应 class 类中的一个方法:

image

其中 UserInfoSql 类如下:

image

然后,再来一个测试用例,把三个方法都测试一下:

image

最后的输出结果如下:

xmlQueryAgeByName whyAge = 18
annotationQueryAgeByName whyAge = 18
classQueryAgeByName whyAge = 18

测试用例就演示完成了,是一个极简的用例。

我就是基于这个案例去分析源码的,在分析之前,其实有点经验的老哥也能看出来了,我们先撇开常规的 xml 文件的形式不谈。

基于 @Select 注解的接口, SQL 就在注解里面,所以我们只需要通过反射取出注解里面的 SQL 进行分析就行了。

基于 @SelectProvider 注解的接口,SQL 虽然在一个类的方法中,但是注解上都告诉你是哪个类的哪个方法了,所以,一定是基于反射去取出方法里面的 SQL 的。

接下来,我们就是去验证一下。

好,准备发车。

image

小心求证

关于 mybatis 我之前写过这篇文章《很开心,在使用mybatis的过程中我踩到一个坑》,其中提到了一个逆向排查法。有兴趣的可以去看一下。

在这篇文章中我们还是来个常规分析吧。本文分析源码为 mybatis 3.4.0 版本。

首先,我先问你一个问题。SpringBoot 是怎么加载 mybatis 的?

image

熟悉 SpringBoot 启动过程的朋友知道,SpringBoot 会去加载mybatis-spring-boot-autoconfigure-x.x.x.jar下 META-INF 中的spring.factories文件:

image

所以,下面的 sqlSessionFactory 方法就是我们的入口处:

image

入口给你找到了,你可以直接在这里加上断点开始 debug 了。

我知道,虽然是刚刚开始,但是可能有些读者觉得已经超纲了。但是没有关系的,继续看下去,我这里只是给你说个入口在哪而已。

image

由于 debug 的过程不是文本重点,这里就不去介绍了。debug 的时候我们会看到这个方法:

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse

这个方法的第 92 行,就是我们的 xml 内容:

image

然后在下面这个方法中对 xml 文件进行疯狂的解析:

org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode

图片可以点开看大图哦,debug 模式,可以看到一些输出:

image

上面的源码的第 94 行,获 取 SqlSource 很关键,要好好看看,这里调用了这个方法:

org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class<?>)

image

接着在下面方法的第 52 行,剥离出整个完整的 sql:

org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode

image

上面就是常规的 xml 形式的 SQL 原始语句(变量、条件表达式都还未进行替换,不可直接执行的 SQL)获取过程,不是本文重点,简单的分析一下就行。

image

接下来继续 debug 的时候会遇到下面这个方法,看包名你就知道,这就是我们关心的注解解析相关的方法了:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse

在这个方法里面,会去循环处理 mapper 类中的方法:

image

接下来,就会遇到这个方法了:

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getSqlSourceFromAnnotations

当循环到 annotationQueryAgeByName 方法的时候,下面方法的一些关键参数如下所示:

image

首先我们看 428 行,解析到了 sqlAnnotationType 为 Select:

image

所以会进入下面的 if 分支,然后运行到 435 行,通过反射获取到了 @Select 注解上的 SQL 语句:

image

继续往下走,通过 436 行,我们可以走到这个方法:

org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, java.lang.String, java.lang.Class<?>)

这个方法就有点意思了,进来判断了 script 即 SQL 是否是以