MyBatis
一. demo环境
使用Maven搭建的项目,MyBatis版本为3.4.6,使用从 XML 中构建 SqlSessionFactory的方式来使用MyBatis,具体可以参考https://github.com/BrightLoong/mylab/tree/master/mybatis-learn,单元测试代码如下:
package io.github.brightloong.mybatis.learn;
import io.github.brightloong.mybatis.learn.mapper.BindRecordMapper;
import io.github.brightloong.mybatis.learn.pojo.BindRecord;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MapperTest {
private SqlSession sqlSession;
@Before
public void before() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
sqlSession = sqlSessionFactory.openSession();
}
@After
public void after() {
sqlSession.close();
}
@Test
public void testSelect() {
BindRecordMapper mapper = sqlSession.getMapper(BindRecordMapper.class);
List<BindRecord> bindRecord = mapper.selectAll();
Assert.assertNotNull(bindRecord);
}
}
二. 概述
从上面的代码可以看出,MyBatis的运行主要围绕以下几个核心组件:
- SqlSessionFactoryBuilder:根据配置信息(比如xml配置信息)或者代码来生成SqlSessionFactory。
- SqlSessionFactory :工厂接口,SqlSessionFactoryBuilder默认生成的是DefaultSqlSessionFactory。
- SqlSession:同样也是接口,MyBatis默认实现是DefaultSqlSession;SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句,也可以通过SqlSession获取Mapper接口,如上面的
BindRecordMapper mapper = sqlSession.getMapper(BindRecordMapper.class);
。 - 映射器实例(Mapper Instances):映射器是一个你创建来绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。一般来说由一个Java接口和XML文件(或者注解)构成,给出对应的SQL和映射实例。
三. 获取SqlSession
看看主要流程的时序图,了解每一步主要在做什么。
1. 获取SqlSession时序图
get sqlSession
- 1.1:xml配置读入为inputStream
- 1.2:实例化SqlSessionFactoryBuilder
- 1.2:调用SqlSessionFactoryBuilder.build,解析xml配置,将xml的配置加载到Configuration供其他对象使用,然后返回SqlSessionFactory
- 1.3:SqlSessionFactory.openSession返回DefaultSqlSession,下面同样通过时序图了解下openSession主要做了那些事
2. SqlSessionFactory.openSession()时序图
SqlSessionFactory.openSession
- 1.1:调用DefaultSqlSessionFactory.openSession()最后实际调用的是openSessionFromDataSource
- 1.1.1-1.12:构造事务相关的一些对象,用于构造Executor
- 1.1.3:Configuration.newExecutor,返回Executor用于构造DefaultSqlSession,Executor是SqlSession的核心依赖对象,用于调度其他对象来处理参数、完成数据库操作和返回结果。
- 1.1.3:返回DefaultSqlSession
三. 获取Mapper
通过BindRecordMapper mapper = sqlSession.getMapper(BindRecordMapper.class);
返回了Mapper实例,调用对应的方法就会执行相应的SQL。我们并没有对BindRecordMapper这个接口进行实现,那么方法又是怎么执行成功的呢。通过调试我们发现,实际返回的是代理类。关于代理的一些概念,请参考之前的文章Java静态代理&动态代理笔记。
实际返回的代理类
通过跟踪源码,发现Mapper通过JDK的动态代理来实现。具体代码在MapperProxyFactory中,代码如下:
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
通过JDK动态代理,返回了MapperProxy。关键代码位于MapperProxy中,通过JDK动态代理相关知识可以知道,当调用被代理对象的方法的时候,实际执行的是代理对象中的invoke()方法。所以当调用mapper的对应方法的时候,实际调用的是MapperProxy中的invoke()方法。
四. 总结
通过上面的分析梳理下MyBatis的核心组件的关联。
核心组件关联