zoukankan      html  css  js  c++  java
  • 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 的访问。

  • 相关阅读:
    C# 操作Word目录——生成、删除目录
    Java PDF页面设置——页面大小、页边距、纸张方向、页面旋转
    【BZOJ3157/3516】国王奇遇记(数论)
    【BZOJ2137】submultiple(数论)
    【CF734F】Anton and School(构造)
    【CF618F】Double Knapsack(构造)
    【BZOJ2034】最大收益(贪心)
    【CF981D】Bookshelves(贪心,动态规划)
    【CF865D】Buy Low Sell High(贪心)
    【BZOJ3716】[PA2014]Muzeum(贪心,网络流)
  • 原文地址:https://www.cnblogs.com/huangzejun/p/8884542.html
Copyright © 2011-2022 走看看