MyBatis源码分析是一个系列,旨在深入了解MyBatis执行流程以及原理。本篇我们先大体的看下MyBatis整个的执行流程,对MyBatis整体有个了解,然后我们在找几个主题深入的去讨论,比如缓存、连接池等,希望通过MyBatis系列,对你有所帮助,那么我们开始吧!
首先每一个框架都会有一个入口,让我们可以很容易的拿来即用,同样也可以沿着入口,了解这个框架的整体流程,然后在深扒底层的原理细节。MyBatis官网提供给我们如何在项目中使用它。1
2
3
4
5
6
7
8
9
10
11String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
}
上述代码是一个典型的MyBatis查询语句,那我们从这几句话开始我们的分析。
首先创建了一个SqlSessionFactory,也就是MyBatis的入口。它的主要功能就是创建SqlSession。
在SqlSessionFactyoryBuilder类中根据提供的XML配置,创建了一个DefaultSqlSessionFactory的实例
通过SqlSessionFactory打开一个SqlSession,首先我们去看下SqlSession接口的描述
1 | SqlSession用于处理MyBatis的主Java接口。 |
SqlSession的方法都是常用的增删改查,事务的回滚提交,以及缓存等,该接口是MyBatis操作的核心。
那我们去看下刚才代码中创建DefaultSqlSessionFactory实例是如何打开一个SqlSession
1 | private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { |
方法返回一个DefaultSqlSession实例,一起创建还有TransactionFactory和Executor实例,跟每个SqlSession绑定。
ExcutorType可以通过XML配置中的setting.defaultExecutorType来自定义。如果没有在配置中指定,会设置一个默认值,在方法XMLConfigBuilder.settingsElement()中可以找到相应的代码。
TransactionFactory的创建是通过Environment.getTransactionFactory()方法来实现的。同样也可以使用xml中setting.transactionManager进行配置配置的。Environment的实例化可以在XMLConfigBuilder.environmentsElement()找到,根据提供的type对应的Class对象反射创建一个TransactionFactory。
1 | private TransactionFactory transactionManagerElement(XNode context) throws Exception { |
XML中setting.transactionManager的配置是JDBC,在TypeAliasRegistry.resolveAlias方法中解析到指定的类。
其中使用的TYPE_ALIASES是一个Map,在TypeAliasRegistry的构造函数中已经初始化了一部分数据,但是我们并没有看到JDBC的key,我们在继续深入查看调用TypeAliasRegistry.registerAlias方法的地方,可以看到Configuration的构造函数中设置了该值:
1 | typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); |
所以上面TransactionFactory实例了一个JdbcTransactionFactory的对象,并根据数据源、事务隔离级别、是否自动提交事务,创建了一个Transaction实例,Transaction包装了数据库连接,处理连接生命周期,包括:创建,准备,提交/回滚和关闭。
接下来创建了一个Executor:
1 | public Executor newExecutor(Transaction transaction, ExecutorType executorType) { |
cacheEnabled可以通过xml中settings.cacheEnabled配置,默认为true,可以在XMLConfigBuilder.settingsElement方法中找到相应代码:
1 | configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); |
创建了一个CachingExecutor实例。最后将三个参数组合创建了一个DefaultSqlSession实例。
然后我们就走到真正的查询是怎么进行的了,那我们继续看代码,我们找一个最简单的selectOne去看下。
1 | public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { |
MappedStatement是读取在xml中配置的Mapper信息,然后解析里面的insert/update/delete/select语句创建的实例。具体实现代码可以在XMLConfigBuilder.parseConfiguration()方法中找到。executor我们在上面提到是创建的CachingExecutor实例,所以代码走到该类的query方法。
1 | public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) |
我们可以看到,代码先去查找MappedStatement对应的缓存,如果找不到缓存会委派到具体的具体的类去查询,可以进入BaseExecutor的query方法,首先他也会查询缓存,这个缓存是SqlSession的本地缓存,如果没有缓存才会从数据库去查找。
至此,一个sql的执行的主流程就到此结束了。