Spring 与 MyBatis 的整合

本文讨论 Spring 与 MyBatis 的整合。

在 beans.xml 中我们定义了两个 bean: SqlSessionFactoryBean、SqlSessionTemplate。

1、SqlSessionFactoryBean

a、SqlSessionFactoryBean 是 FactoryBean,它在 Spring 容器中通过 getObject() (单例模式)返回 SqlSessionFactory bean。源码如下:

1   public SqlSessionFactory getObject() throws Exception {
2     if (this.sqlSessionFactory == null) {
3       afterPropertiesSet();
4     }
5 
6     return this.sqlSessionFactory;
7   }

可以看出,SqlSessionFactoryBean 对 sqlSessionFactory 是懒加载。

b、afterPropertiesSet() 源码如下:

1   public void afterPropertiesSet() throws Exception {
2     notNull(dataSource, "Property 'dataSource' is required");
3     notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
4 
5     this.sqlSessionFactory = buildSqlSessionFactory();
6   }

afterPropertiesSet() 先检查 dataSource、sqlSessionFactoryBuilder 是否为 null。

dataSource 在 beans.xml 中设置,sqlSessionFactoryBuilder 在新建 sqlSessionFactoryBean 时会自动创建。

c、protected SqlSessionFactory buildSqlSessionFactory() throws Exception;

这个方法设置了 transactionFactory (默认为 SpringManagedTransactionFactory),生成 environment,放入 configuration 中。

然后对 mapperLocations(Resource[]) 进行解析,放入 configuration 的 mappedStatements 中。

最后调用 sqlSessionBuilder.build(configuration) 返回 sqlSessionFactory。

d、在 beans.xml 中给 SqlSessionFactoryBean 设置了 dataSource、mapperLocations 两个参数。

2、SqlSessionTemplate 构造方法接收一个 SqlSessionFactory 参数,最终会调用以下的构造方法:

 1 public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
 2       PersistenceExceptionTranslator exceptionTranslator) {
 3 
 4     notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
 5     notNull(executorType, "Property 'executorType' is required");
 6 
 7     this.sqlSessionFactory = sqlSessionFactory;
 8     this.executorType = executorType;
 9     this.exceptionTranslator = exceptionTranslator;
10     this.sqlSessionProxy = (SqlSession) newProxyInstance(
11         SqlSessionFactory.class.getClassLoader(),
12         new Class[] { SqlSession.class },
13         new SqlSessionInterceptor());
14   }

sqlSessionFactory 默认 executorType 为 Simple,exceptionTranslator 为 MyBatisExceptionTranslator。

SqlSessionTemplate 可以加入到 Spring 的事务中,且线程安全,而 DefaultSqlSession 没有这两个特性。

3、DAO 层对数据库的访问最后都被 SqlSessionInterceptor 拦截。

这里用到了 JDK 反射功能。

private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 获取sqlSession,其中包含了statement id
      final 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) {
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        // 关闭session
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }

可以看出,对 sqlSession 的获取和关闭、事务管理、异常管理都由这个内部类 SqlSessionInterceptor 负责;业务层只需专注 SQL 的编写,无需再关注对资源、事务及异常的管理。

代理的目的是加入透明的对资源、事务、异常的管理。

最终委托给 DefaultSqlSession 去访问数据库:从 configuration 获取 MappedStatement,由 Executor(默认 CachingExecutor)完成对 DB 的访问。

原文地址:https://www.cnblogs.com/huangzejun/p/8884542.html