mybatis集成spring

目录

一. mybatis开放接口

SqlSession 初始化

String resource = "mybatis-config.xml";  
InputStream inputStream = Resources.getResourceAsStream(resource);  
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
SqlSession sqlSession = sqlSessionFactory.openSession();  
List list = sqlSession.selectList("com.foo.bean.BlogMapper.queryAllBlogInfo");

Mybatis的初始化就是加载配置文件,构造Configuration对象:1. 解析子节点的方法mapperElements(root.evalNode(“mappers”)), 以key=”com.foo.bean.BlogMapper.queryAllBlogInfo” ,value为MappedStatement对象的形式维护到Configuration的一个Map中;2. 解析environments节点,创建事务工厂 TransactionFactory和数据源DataSource

传统的MyBatis提供的API—SqlSession

SqlSession是MyBatis提供的顶层接口,底层实现简单描述如下:
(1、根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;
(2、为查询创建缓存,以提高性能;
(3、创建JDBC的Statement连接对象,传递给StatementHandler对象,返回List查询结果。

List/int/void sqlSession.select/selectList/insert/update/delete(statementId, paramObject)

使用Mapper接口—通过动态代理实现对SqlSession接口的封装

SqlSession sqlSession = SessionFactory.getSqlSession(resource); 
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

对UserMapper接口的调用通过动态代理最终还是调到SqlSession接口,下面看下如何构造代理对象:

  • sqlSession.getMapper(UserMapper.class) => mapperRegistry.getMapper(type, sqlSession) => mapperProxyFactory.newInstance(sqlSession)
    mapperRegistry保存UserMapper接口到mapperProxyFactory实例的映射,mapperProxyFactory可以创建mapperProxy对象
    然后交由MapperMethod将参数传递给sqlSession,调用sqlSession的接口

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
    }
    public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    if (SqlCommandType.INSERT == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
    }

二. MapperScannerConfigurer自动扫描包

MapperFactoryBean返回mapper接口

MapperFactoryBean实现FactoryBean,实际返回的是mapper接口的代理实现,即其实现作为bean被加入到spring容器中

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

MapperFactoryBean调用configuration.addMapper将配置的接口mapperInterface加入到Configuration,MapperFactoryBean是如何找到mapper的配置文件呢?

Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } catch (Throwable t) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
        throw new IllegalArgumentException(t);
      } finally {
        ErrorContext.instance().reset();
      }
    }

configuration.addMapper中会调用parse方法,加载mapper文件,mapper文件的地址默认与Mapper接口路径一致

public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          parseStatement(method);
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

SqlSessionFactoryBean创建Configuration和SqlSessionFactory,参考

  • 首先初始化一个configuration,设置别名,装入插件,解析mybatis.xml文件(若配置了configLocation)
  • 然后创建事物工厂SpringManagedTransactionFactory(也可配置自定义的实现),新建一个Environment对象,并将新建的transactionFactory放入其中
  • 解析配置的sql mapper配置文件(若有mapperLocations), 解析sql mapper文件中的映射关系生成MappedStatement对象,并执行 configuration.addMappedStatement(statement), 放入到configuration对象中

可以看到整个过程就是初始化configuration然后创建SqlSessionFactory并交给spring容器

实现了BeanDefinitionRegistryPostProcessor接口的MapperScannerConfigurer

可以想象postProcessBeanDefinitionRegistry方法实现就是创建一个个mapper接口类型的BeanDefinition(实际是MapperFactoryBean),然后注册到spring容器中
根据MapperScannerConfigurer节点配置的basePackage,扫描包下的所有接口,包装成MapperFactoryBean的实现类,注入sqlSessionFactory或sqlSessionTemplate对象; 这样,在spring初始化MapperFactoryBean时,就能返回这些mapper接口的动态代理实现

for (BeanDefinitionHolder holder : beanDefinitions) {
        GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

        if (logger.isDebugEnabled()) {
          logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
              + "' and '" + definition.getBeanClassName() + "' mapperInterface");
        }

        // the mapper interface is the original class of the bean
        // but, the actual class of the bean is MapperFactoryBean
        definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
        definition.setBeanClass(MapperFactoryBean.class);

        definition.getPropertyValues().add("addToConfig", this.addToConfig);

        boolean explicitFactoryUsed = false;
        if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
          definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
          explicitFactoryUsed = true;
        } else if (this.sqlSessionFactory != null) {
          definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
          explicitFactoryUsed = true;
        }

        if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
          if (explicitFactoryUsed) {
            logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
          }
          definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
          explicitFactoryUsed = true;
        } else if (this.sqlSessionTemplate != null) {
          if (explicitFactoryUsed) {
            logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
          }
          definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
          explicitFactoryUsed = true;
        }

        if (!explicitFactoryUsed) {
          if (logger.isDebugEnabled()) {
            logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
          }
          definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
      }

spring自动扫描机制

 *  通过反射加载路径下的所有类, 这种反射方式较慢,并且没有得到lazyload的好处
 *  加载源文件,分析文件属性,这种方式情况复杂,可能需要词法分析
 *  spring使用到了第三种方式,通过asm对字节码的处理得到响应的属性

三. Spring事物

MapperFactoryBean的sqlSessionTemplate

sqlSessionTemplate是MapperFactoryBean成员sqlsession动态代理实现,最终会调用到SqlSessionInterceptor
首先执行getSqlSession方法,执行步骤如下:

  • 第一次执行将进入到sessionFactory.openSession(executorType)
  • 然后进入到mybatis的DefaultSqlSessionFactory.openSessionFromDataSource
  • 根据之前初始化configuration时的配置得到SpringManagedTransactionFactory,创建SpringManagedTransaction
  • 生成一个Executor,configuration.newExecutor(tx, execType, autoCommit),返回new DefaultSqlSession(configuration, executor),即上一步得到的SpringManagedTransaction作为sqlsession的事物;在Executor执行方法中获取connection将通过SpringManagedTransaction得到
  • DataSourceUtils.getConnection(this.dataSource)获取connection,过程和getSqlSession方法类似: 在TransactionSynchronizationManager中查找,有则返回,没有则创建一个(三种方式:unpooleddatasource,pooleddatasource,jndidatasource),并绑定到TransactionSynchronizationManager

获取到SqlSession以后,代理SqlSession接口中的方法,调用closeSqlSession

private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

spring的TransactionSynchronizationManager

TransactionSynchronizationManager用于辅助实现spring的事物传播机制,使用ThreadLocal持有每个线程的connection\Transaction等.方法执行时判断当前线程是否已开启事物,然后根据配置的事物传播方式开启事物,挂起事物,或者抛出异常,具体可参考TransactionInterceptor的invoke方法

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值