Mybatis核心概念

Configuration 管理mysql-config.xml全局配置关系类

SqlSessionFactorySession管理工厂接口

Session SqlSession是一个面向用户(程序员)的接口。SqlSession中提供了很多操作数据库的方法

Executor 执行器是一个接口(基本执行器、缓存执行器)作用:SqlSession 内部通过执行器操作数据库

MappedStatement 底层封装对象作用:对操作数据库存储封装,包括 sql 语句、输入输出参数

StatementHandler 具体操作数据库相关的 handler 接口

SpringBoot整合Mybatis原理分析_Mybatis

SpringBoot整合Mybatis原理分析_Mybatis_02

Mybatis编程式用例

public void testSelect() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    SqlSession session = sqlSessionFactory.openSession(); // ExecutorType.BATCH
    try {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Blog blog = mapper.selectBlogById(1);
        System.out.println(blog);
    } finally {
        session.close();
    }
}

配置扫描解析过程

1、springboot使用自动装配原理注入SqlSessionFactory,对应方法路径为org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration#sqlSessionFactory。如果使用的是mybatis-config.xml进行配置,那么在上述方法中会使用XMLConfigBuilder对mybatis-config.xml文件进行解析,并且会把mybatis-config.xml文件中配置的一个个mapper bean生成相应的mapperProxyFactory并且放入mapperRegistry中.MapperRegistry里面维护的其实是一个Map容器,存储接口和代理工厂的映射关系。

2、

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan

 

@MapperScan是一个复合注解,其中@Import注解便是起到了扫描注册第三方组件到springIOC的作用。MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,实现了registerBeanDefinitions方法,主要功能用于动态的注册某一些具有相同特征的一批类到Spring IoC

3.

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
          LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
          processBeanDefinitions(beanDefinitions);
        }
        return beanDefinitions;
    }
}

 

  • super.doScan()方法扫描@MapperScan注解中对应的Mapper路径,把该路径下所有符合候选条件的接口mapper放入ioc容器中。

  • processBeanDefinitions(beanDefinitions);该方法在doScan()之后进行调用,将容器中mapper bean的真正类型修改为MapperFactoryBean类型。

  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // 构造函数传参,传入初始mapper类型
      definition.setBeanClass(this.mapperFactoryBeanClass);
}

 

获得Mapper对象

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> 

 

1、SqlSessionDaoSupport内部持有SqlSession的实现类SqlSessionTemplate;

2、SqlSessionDaoSupport类重写了checkDaoConfig()方法,这个方法主要用于非mybatis-config.xml方式来注册mapper的情况,这个方法会确保把所有的mapper bean生成相应的mapperProxyFactory并且放入mapperRegistry中。调用时机:在Bean的属性值设置完成时被调用(InitializingBean接口)

3、上述说到所有的mapper接口在放入spring容器之后,真正的bean类型被修改为MapperFactoryBean,而MapperFactoryBean实现了FactoryBean接口,所以程序中从ioc容器在获取对应的Mapper实例的话,走的是getObject()方法,最终调用的又是org.apache.ibatis.binding.MapperRegistry#getMapper方法;

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

 

4、可以看到获取Mapper的代理对象,实际上是从map中获取对应的工厂类后,最终通过JDK动态代理创建的。实质上是获取了一个MapperProxy的代理对象,MapperProxy中有SqlSession、mapperInterface、methodCache。

执行sql

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
}

 

1、由于所有的Mapper都是MapperProxy的代理对象,所以任意的方法都是执行MapperProxy的invoke()方法。

2、通过Executor、MappedStatement、SqlSession进行sql语句处理,参数填充等等,最终还是通过JDBC包中的PreparedStatement、ResultSet进行最终的调用和结果集处理;

————————————————