Devin's Blogs

个人点滴记录

简介

MyBatis 源码系列对于新手来说是一个有趣且具有挑战性的学习项目。深入研究 MyBatis 源码将使您更好地理解 Java 持久性框架的内部工作原理,并提高编程技能。

目录

Mybatis 源码之入门

Mybatis 源码之加载配置

Mybatis 源码之获取映射器类

Mybatis 源码之 Mapper 实例

Mybatis 源码之查询数据库

Mybatis 源码之数据库结果集

Mybatis 源码之更新数据库

Mybatis 源码整合 Spring

Mybatis 设计模式

注: 源码相关版本

  • Mybatis: 3.5.8

  • Mybatis-Spring: 2.0.3

参考链接

官网
源码
mybatis-Spring 官网
mybatis-Spring 源码

构建 SqlSessionFactory

从 XML 中构建

从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。

1
2
3
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

不使用 xml 构建

1
2
3
4
5
6
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

Spring 方式 构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class MyBatisConfig {

@Bean
public DataSource DataSource() {
// 构建省略
return null;
}

@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
return factoryBean.getObject();
}
}

作用域(Scope)和生命周期

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。
如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

1
2
3
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

映射器实例

映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。
也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。
尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:

1
2
3
4
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的应用逻辑代码
}

通过 SqlSessionFactoryBuilder 加载配置

定位: org.apache.ibatis.session.SqlSessionFactoryBuilder#build
作用: 解析xml文件, 并生成配置项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 准备工作:将配置文件加载到内存中并生成一个document对象 ,同时初始化Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}

xml文件解析器 XMLConfigBuilder

定位: org.apache.ibatis.builder.xml.XMLConfigBuilder#XMLConfigBuilder
作用: 解析 xml 配置文件

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
// 上面的6个构造函数最后都会合流到这个函数,传入XPathParser
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 调用父类初始化configuration
super(new Configuration());
// 错误上下文设置成SQL Mapper Configuration(xml文件配置)
ErrorContext.instance().resource("SQL Mapper Configuration");
// 将Properties全部设置到configuration里面去
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
// 解析配置
public Configuration parse() {
// 根据parsed变量的值判断是否已经完成了对mybatis-config.xml配置文件的解析
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 在mybatis-config.xml配置文件中查找<configuration>节点,并开始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
// 解析配置
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
// 解析properties
propertiesElement(root.evalNode("properties"));
// 解析settings
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 设置vfsImpl字段
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 解析类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 解析插件
pluginElement(root.evalNode("plugins"));
// 对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
// 对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射工厂
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);//设置具体的属性到configuration对象
// read it after objectFactory and objectWrapperFactory issue #631
// 环境
environmentsElement(root.evalNode("environments"));
// databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
// 映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
// 未指定XMLConfigBuilder.environment字段,则使用default属性
if (environment == null) {
environment = context.getStringAttribute("default");
}
// 遍历子节点
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
// 与XmlConfigBuilder.environment字段匹配
if (isSpecifiedEnvironment(id)) {
// 创建TransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 创建DataSourceFactory和DataSource
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// 创建Environment
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 将Environment对象记录到Configuration.environment字段中
configuration.setEnvironment(environmentBuilder.build());
break;
}
}
}
}
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
// 根据type="POOLED"解析返回适当的DataSourceFactory
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 处理mapper子节点
for (XNode child : parent.getChildren()) {
// package子节点
if ("package".equals(child.getName())) {
// 自动扫描包下所有映射器
String mapperPackage = child.getStringAttribute("name");
// 扫描指定的包,并向mapperRegistry注册mapper接口
configuration.addMappers(mapperPackage);
} else {
// 获取mapper节点的resource、url、class属性,三个属性互斥
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// 如果mapper节点指定了resource或者url属性,则创建XmlMapperBuilder对象,并通过该对象解析resource或者url属性指定的mapper配置文件
if (resource != null && url == null && mapperClass == null) {
// 使用类路径
ErrorContext.instance().resource(resource);
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
// 创建XMLMapperBuilder对象,解析映射配置文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url != null && mapperClass == null) {
// 使用绝对url路径
ErrorContext.instance().resource(url);
try (InputStream inputStream = Resources.getUrlAsStream(url)) {
// 创建XMLMapperBuilder对象,解析映射配置文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url == null && mapperClass != null) {
// 如果mapper节点指定了class属性,则向MapperRegistry注册该mapper接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 直接把这个映射加入配置
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}

Mapper 解析器

定位: org.apache.ibatis.builder.xml.XMLMapperBuilder
作用: 用户解析 mapper 节点, 并封装成 Mapper 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 解析
public void parse() {
// 判断是否已经加载过该映射文件
if (!configuration.isResourceLoaded(resource)) {
// 处理mapper节点
configurationElement(parser.evalNode("/mapper"));
// 将resource添加到Configuration.loadedResources集合中保存,他是hashset类型的集合,其中记录了已经加载过的映射文件
configuration.addLoadedResource(resource);
// 绑定映射器到namespace
bindMapperForNamespace();
}

// 处理ConfigurationElement方法中解析失败的resultMap节点
parsePendingResultMaps();
// 处理ConfigurationElement方法中解析失败的cache-ref节点
parsePendingCacheRefs();
// 处理ConfigurationElement方法中解析失败的SQL语句节点
parsePendingStatements();
}

Mybatis 配置项 Configuration

定位: org.apache.ibatis.session.Configuration#Configuration
作用: 解析后的参数配置项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Configuration {
// 脚本语言注册器
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
//类型别名注册机
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
// 。。。 省略别名注册

languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
}

获取 SqlSession 对象

定位: org.apache.ibatis.session.SqlSessionFactory#openSession
作用: 创建 SqlSession 实例

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取 mybatis-config.xml 配置文件中配置的 Environment 对象
final Environment environment = configuration.getEnvironment();
// 获取 TransactionFactory 对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建 Transaction 对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据配置创建 Executor 对象
final Executor executor = configuration.newExecutor(tx, execType);
// 然后产生一个 DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
//如果打开事务出错,则关闭它
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
//最后清空错误上下文
ErrorContext.instance().reset();
}
}
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
// 获取当前连接的事务是否为自动提交方法
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
// 当前数据库驱动提供的连接不支持事务,则可能会抛出异常
autoCommit = true;
}
// 获取 mybatis-config.xml 配置文件中配置的 Environment 对象
final Environment environment = configuration.getEnvironment();
// 获取 TransactionFactory 对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建 Transaction 对象
final Transaction tx = transactionFactory.newTransaction(connection);
// 根据配置创建 Executor 对象
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

创建执行器

定位: org.apache.ibatis.session.Configuration#newExecutor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 产生执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 根据参数,选择合适的 Executor 实现
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 根据配置决定是否开启二级缓存的功能
if (cacheEnabled) {
// 装饰器模式
executor = new CachingExecutor(executor);
}
// 此处引入插件, 通过插件可以改变 Executor 行为
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

DefaultSqlSession 对象

DefaultSqlSession 实现了 SqlSession 接口的所有方法,包括对数据库的增删改查、事务管理等功能。

获取 Mapper 实列

作用: 通过 SqlSession 对象, 查找当前 Mybatis 的 mapper 注册中心指定的 Mapper 类型, 单据 Mapper工厂, 通过工厂创建 Mapper 实例

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
33
34
35
36
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

// org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
public <T> T getMapper(Class<T> type) {
//最后会去调用MapperRegistry.getMapper
return configuration.getMapper(type, this);
}

// org.apache.ibatis.binding.MapperRegistry#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 查找指定type对应MapperProxyFactory对象
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 如果mapperProxyFactory为空,则抛出异常
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 创建实现了type接口的代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

// org.apache.ibatis.binding.MapperProxyFactory#newInstance
protected T newInstance(MapperProxy<T> mapperProxy) {
// 创建实现了mapperInterface接口的代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
// org.apache.ibatis.binding.MapperProxyFactory#newInstance
public T newInstance(SqlSession sqlSession) {
// 创建MapperProxy对象,每次调用都会创建新的mapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

MapperProxy

定位: org.apache.ibatis.binding.MapperProxy

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class MapperProxy<T> implements InvocationHandler, Serializable {

private static final long serialVersionUID = -4724728412955527868L;
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
private static final Constructor<Lookup> lookupConstructor;
private static final Method privateLookupInMethod;
// 记录了关联的sqlSession对象
private final SqlSession sqlSession;
// mapper接口对应的Class对象
private final Class<T> mapperInterface;
// 用于缓存MapperMethod对象,其中key是mapper接口中方法对应的Method对象,value是对应的MapperMethod对象,MapperMethod对象会完成参数转换
// 以及SQL语句的执行功能,需要注意的是,MapperMethod中并不记录任何状态相关的信息,所以可以在多个代理对象之间共享
private final Map<Method, MapperMethodInvoker> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}

static {
Method privateLookupIn;
try {
// jdk9 以上的私有方法处理
// privateLookupInMethod 判断MethodHandles类中是否有privateLookupIn方法,该方法是java9中才有的
privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
} catch (NoSuchMethodException e) {
privateLookupIn = null;
}
privateLookupInMethod = privateLookupIn;

Constructor<Lookup> lookup = null;
if (privateLookupInMethod == null) {
// JDK8 私有方法
try {
lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookup.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
e);
} catch (Exception e) {
lookup = null;
}
}
lookupConstructor = lookup;
}
}

Mapper 实例对象执行查询

在拿到 Mapper 实例后,直接调用查询接口 Emp emp = mapper.findEmpByEmpno(1);, 实际上调用了其代理对象 MapperProxy 的 invoke 方法。

定位: org.apache.ibatis.binding.MapperProxy#invoke

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
33
34
35
36
37
38
39
40
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果目标方法继承自Object,则直接调用目标方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 根据被调用接口方法的method对象,从缓存中获取MapperMethodInvoker对象,如果没有则创建一个并放入缓存,然后调用invoke
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 获取缓存中MapperMethodInvoker,如果没有则创建一个,而MapperMethodInvoker内部封装这一个MethodHandler
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {
// 如果调用接口的是默认方法
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 如果调用的普通方法(接口),则创建一个 PlainMethodInvoker 并放入缓存,其中 MapperMethod 保存对应接口方法的 SQL 以及入参和出参的数据类型等信息
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}

创建 MapperMethod

定位: org.apache.ibatis.binding.MapperMethod

1
2
3
4
5
6
7
// MapperMethod 构造函数
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// 记录了 SQL 语句的名称和类型
this.command = new SqlCommand(config, mapperInterface, method);
// Mapper 接口中对应方法的相关信息(如参数列表, 返回值)
this.method = new MethodSignature(config, mapperInterface, method);
}

创建 SqlCommand

定位: org.apache.ibatis.binding.MapperMethod$SqlCommand

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
// 从配置中获取 MappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
if (ms == null) {
// 处理@Flush注解
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// 初始化name和type
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
// 解析 MappedStatement
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// SQL语句的名称是由Mapper接口的名称与对应的方法名称组成的
String statementId = mapperInterface.getName() + "." + methodName;
// 检测是否有该名称的SQL语句
if (configuration.hasStatement(statementId)) {
// 从configuration.mappedStatements集合中查找对应的MappedStatement对象,MappedStatement对象中封装了SQL语句相关的信息,在mybatis初始化时创建
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
// 处理 Mapper 对象的所有接口
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}

创建 MethodSignature

定位: org.apache.ibatis.binding.MapperMethod$MethodSignature

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
// 解析方法的返回值类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
// 初始化returnsVoid、returnsMany、returnsCursor、returnsOptional等参数
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
// 若MethodSignature对应方法的返回值是Map且制定了@MapKey注解,则使用getMapKey方法处理
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
// 初始化rowBoundsIndex、resultHandlerIndex字段
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 创建参数名称解析器, 给参数列表命名
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
private String getMapKey(Method method) {
String mapKey = null;
if (Map.class.isAssignableFrom(method.getReturnType())) {
// 如果返回类型是map类型,查看该method是否有MapKey注解,如果有,则将这个注解的值作为map的key
final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
if (mapKeyAnnotation != null) {
mapKey = mapKeyAnnotation.value();
}
}
return mapKey;
}
}
private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
Integer index = null;
final Class<?>[] argTypes = method.getParameterTypes();
// 遍历MethodSignature对应的方法的参数列表
for (int i = 0; i < argTypes.length; i++) {
// 记录paramType类型参数在参数列表中的位置索引
if (paramType.isAssignableFrom(argTypes[i])) {
if (index == null) {
index = i;
// RowBounds和ResultHandler类型的参数只能有一个,不能重复出现
} else {
throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
}
}
}
return index;
}

创建 MethodSignature

定位: org.apache.ibatis.reflection.ParamNameResolver
作用: 解析 Mapper 接口方法的参数列表的名称

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
* aMethod(@Param("M") int a, @Param("N") int b) -> {{0, "M"}, {1, "N"}}
* aMethod(int a, int b) -> {{0, "0"}, {1, "1"}}
* aMethod(int a, RowBounds rb, int b) -> {{0, "0"}, {2, "1"}}
**/
private final SortedMap<Integer, String> names;
public ParamNameResolver(Configuration config, Method method) {
this.useActualParamName = config.isUseActualParamName();
// 获取参数列表中每个参数的类型
final Class<?>[] paramTypes = method.getParameterTypes();
// 获取参数列表上的注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
// 该集合用于记录参数索引与参数名称的对应关系
final SortedMap<Integer, String> map = new TreeMap<>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
// 遍历方法所有参数
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
// 如果参数是 RowBounds 类型或 ResultHandler 类型,则跳过对该参数的分析
continue;
}
String name = null;
// 遍历该参数对应的注解集合
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
// @Param 注解出现过一次,就将 hasParamAnnotation 初始化为 true
hasParamAnnotation = true;
// 获取 @Param 注解指定的参数名称
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
// 该参数没有对应的 @Param 注解,则根据配置决定是否使用参数实际名称作为其名称
if (useActualParamName) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
// 使用参数的索引作为其名称
name = String.valueOf(map.size());
}
}
// 记录到map中保存
map.put(paramIndex, name);
}
// 初始化name集合
names = Collections.unmodifiableSortedMap(map);
}
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
// 五参数,返回null
if (args == null || paramCount == 0) {
// 如果没参数
return null;
// 未使用@Param且只有一个参数
} else if (!hasParamAnnotation && paramCount == 1) {
// 如果只有一个参数
Object value = args[names.firstKey()];
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
// 处理使用@Param注解指定了参数名称或者多个参数的情况
} else {
// param这个map记录了参数名称与实参之间的对应关系,ParamMap继承了HashMap,如果向paramMap中添加已经存在的key,会报错,
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 将参数名称与实参对应关系记录到param中
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
// 为参数创建param+索引格式的默认参数名称,并添加到param集合中
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
// 如果@param注解指定的参数名称就是param+索引格式的,则不需要添加
if (!names.containsValue(genericParamName)) {
// 再加一个#{param1},#{param2}...参数
//你可以传递多个参数给一个映射器方法。如果你这样做了,
//默认情况下它们将会以它们在参数列表中的位置来命名,比如:#{param1},#{param2}等。
//如果你想改变参数的名称(只在多参数情况下) ,那么你可以在参数上使用@Param(“paramName”)注解。
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}

执行 MapperMethod

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 根据SQL语句的类型调用SqlSession对应的方法
switch (command.getType()) {
case INSERT: {
// 使用 ParamNameResolver 处理 args 数组,将用户传入的实参与指定参数名称关联起来
// org.apache.ibatis.reflection.ParamNameResolver#getNamedParams
Object param = method.convertArgsToSqlCommandParam(args);
// 调用sqlSession.insert方法,rowCountResult方法会根据method字段中记录的方法的返回值类型对结果进行转换
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
// 处理返回值为void是ResultSet通过ResultHandler处理的方法
if (method.returnsVoid() && method.hasResultHandler()) {
// 如果有结果处理器
executeWithResultHandler(sqlSession, args);
result = null;
// 处理返回值为集合和数组的方法
} else if (method.returnsMany()) {
// 如果结果有多条记录
result = executeForMany(sqlSession, args);
// 处理返回值为map的方法
} else if (method.returnsMap()) {
// 如果结果是map
result = executeForMap(sqlSession, args);
// 处理返回值为cursor的方法
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
// 处理返回值为单一对象的方法
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
// 此方法对返回值的类型进行安全检查
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
// Mapper接口中相应方法的返回值为void
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
// mapper接口中相应方法的返回值为int或者Integer
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
// mapper接口中相应方法的返回值为long或者Long
result = (long) rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
// mapper接口中相应方法的返回值为boolean或者Boolean
result = rowCount > 0;
} else {
// 以上条件都不成立,抛出异常
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
}
return result;
}
0%