Mybatis源码解析

Mybatis源码解析

查询测试Demo

/**
 * 获取SqlSessionFactory对象
 *
 * @return
 * @throws IOException
 */
private SqlSessionFactory getSqlSessionFactory() throws IOException {
    return new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
@Test
public void testQuery() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee empById = employeeMapper.getEmpById(2);
        System.out.println(empById);
        System.out.println(empById.getDept());
        sqlSession.commit();
    } catch (Exception e) {
    	e.printStackTrace();
    } finally {
    	sqlSession.close();
    }
}

获取SqlSessionFactory

  1. 创建XMLConfigBuilder实例,解析mybatis全局配置文件及所有SQL映射文件

    调用构造器创建XMLConfigBuilder实例时,在构造器中调用Configuration的无参构造器创建了一个Configuration对象作为自己的属性

  2. 解析mybatis全局配置文件

    解析settings、typeAliases、mappers、… 标签,并将信息封装到XMLConfigBuilder实例的Configuration对象属性中。

  3. 解析SQL映射文件

    即第2步中的解析mappers标签的核心流程

    1. 创建XMLMapperBuilder实例,解析SQL映射文件

      从mappers标签中解析出mapper映射文件的路径,创建流并创建XMLMapperBuilder实例

    2. 解析cache、cache-ref、select、update、delete、insert、… 标签

      针对select、update、delete、insert标签,解析标签中的每一个属性及标签体中的sql(包括解析动态sql),封装为一个MappedStatement对象,添加到configuration对象的mappedStatements(Map)中。

  4. 创建SqlSessionFactory实现类对象并返回

    利用XMLConfigBuilder实例、XMLMapperBuilder实例解析完mybatis全局配置文件以及SQL映射文件之后,返回XMLConfigBuilder实例中的Configuration对象,利用返回的Configuration对象,作为DefaultSqlSessionFactory的构造器参数,创建DefaultSqlSessionFactory对象返回。

获取SqlSession

  1. 调用SqlSessionFactory实例的openSession方法

    1. 通过SqlSessionFactory实例中的Configuration对象,获取当中的DataSource信息开启事务,创建并返回一个Transaction对象。

      这里开启事务是指逻辑上的开启事务,Transaction对象中的并没有数据库连接,仅仅是设置了Transaction对象的autoCommit属性值为false。

    2. 利用Configuration对象创建Executor

      根据ExecutorType创建对应的Executor,该Executor中初始化了一个本地缓存PerpetualCache,并封装了上一步中创建的事务对象。若还配置了二级缓存,则会将用CachingExecutor包装刚创建的Executor

    3. 调用拦截器链的pluginAll方法包装Executor

      executor = (Executor) interceptorChain.pluginAll(executor);

  2. 创建DefaultSqlSession对象并返回

    DefaultSqlSession对象封装了Configuration对象以及步骤1中创建的Executor

获取Mapper

  1. 调用SqlSession对象的getMapper方法,传入对应Mapper的Class对象。

  2. 调用Configuration对象的getMapper方法。

  3. 调用MapperRegistry对象的getMapper方法,利用动态代理创建了一个Mapper代理实例并返回。

    MapperRegistry中获取了一个Mapper代理工厂MapperProxyFactory,通过代理工厂,利用动态代理技术创建了一个Mapper代理实例并返回。

// 1 ---- TestCase
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 2 ---- DefaultSqlSession
public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
}
// 3 ---- Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
// 4 ---- MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    return mapperProxyFactory.newInstance(sqlSession);
}
// 5 ---- MapperRegistry
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

执行查询

  1. Method对象包装成MapperMethod对象

    上一步获取Mapper的时候,得到的是一个mapper代理对象,在调用代理对象方法的时候,会先把Method对象包装成MapperMethod对象,然后调用其execute方法。

  2. 执行查询

    1. 判断要操作的类型,调用SqlSession对象的查询方法

    2. 获取当前操作对应的Sql信息

      这里先获取与当前操作对应的MappedStatement对象,该对象在获取SqlSessionFactory的时候,就已经通过解析对应的Mapper映射文件创建了出来,一个MappedStatement对象封装一个select|update|delete|insert语句信息。然后把查询参数与MappedStatement对象中Sql信息绑定到一起并返回BoundSql对象。

    3. 生成CacheKey对象

    4. 若配置了二级缓存,则拿CacheKey对象去二级缓存中查询,查询到则直接返回

    5. 若在二级缓存中没查到,再拿CacheKey对象从一级缓存中查找,查询到则直接返回

    6. 若在一级缓存中也没查到,则从数据库查询。查询到之后,先把结果放进一级缓存,然后在放进二级缓存,键都是CacheKey对象,最后返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值