大致流程
MyBatis的基本使用流程为:
- 创建SqlSessionFactory
- 创建SqlSession
- 通过SqlSession创建Mapper代理
- 使用Mapper代理执行Mapper接口方法。
下面将针对这四步进行源码解析。源码版本为3.3.0。
创建SqlSessionFactory
SqlSessionFactory顾名思义,就是用来创建SqlSession的。创建语句也很简单
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
其中build方法就是读取xml配置并生成Configuration对象,并将其赋给SqlSessionFactory对象。这里仅展示两个核心方法:
{% codeblock lang:Java %} public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // 开始解析,从configuration根结点开始 parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { // 解析是有先后顺序的,所以写xml的时候需要注意子结点的顺序 propertiesElement(root.evalNode("properties")); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); ... } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } {% endcodeblock %}创建SqlSession
SqlSession是mybatis的核心,既可以执行SQL并返回结果,也可以获取Mapper对象。创建语句为:
SqlSession sqlSession = sqlSessionFactory.openSession();
再往里看:
1 | private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { |
代码逻辑也比较简单,需要注意的是直到这里,还是没有真正建立JDBC连接。
获得Mapper代理
Mapper代理的获得也很简洁:
XXXMapper mapper = sqlSession.getMapper(XXXMapper.class);
那么Mapper代理是如何获得的呢,总结来说就是通过JDK动态代理生成的,具体如下
1 | public <T> T getMapper(Class<T> type, SqlSession sqlSession) { |
关于这部分,其实一开始有个小疑问,既然MyBatis把缓存发挥到了极致,那为什么不缓存MapperProxy和Mapper代理对象呢?我个人觉得原因可能是:由于SqlSession不是线程安全的,所以较合适的作用域是方法作用域。既然是方法作用域,也就不会多次调用getMapper方法,所以做不做缓存也就无所谓了。
执行Mapper接口方法
执行Mapper方法xxxMapper.selectById(1L);
其内部执行过程相当复杂。
调用MapperProxy的invoke方法:
1 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
mapperMethod是怎么来的?
先从缓存中查找有没有对应的MapperMethod对象,若没有,则新建并存入缓存。
1 | private MapperMethod cachedMapperMethod(Method method) { |
mapperMethod如何执行的
注意:以下代码并不是连续的,这里只选了相对关键的代码。
1 | public Object execute(SqlSession sqlSession, Object[] args) { |
接下来一步步debug,直到:
1 | public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { |
接下来继续debug…
1 | public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) |
继续debug…
1 | public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { |
往下debug n步会发现:
1 | public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { |
接下来就是根据连接进行查询了。
小结
由于这部分内容有点多,稍微进行一下总结,在执行Mapper接口方法时:
- 先判断是否有该方法的MapperMethod对象存在。
- 若没有,则创建。
- 在MapperMethod对象执行方法时。
- 参数转化等。
- 先查询二级缓存(若开启),命中则返回。
- 再查询一级缓存,命中则返回。
- 执行数据库操作,根据当前连接池情况获得连接,并执行操作,返回结果。
MyBatis xml模板
以下提供了一些MyBatis会使用到的xml配置文件
mybatis-config.xml
1 |
|
mapper.xml
1 |
|