Mybatis与Spring集成:SqlSessionTemplate[通俗易懂]

Mybatis与Spring集成:SqlSessionTemplate[通俗易懂]Mybatis与Spring集成:SqlSessionTemplateSqlSessionTemplate构造方法publicSqlSessionTemplate(SqlSessionFactorysqlSessionFactory,ExecutorTypeexecutorType,PersistenceExceptionTranslatorexceptionTra…

大家好,又见面了,我是你们的朋友全栈君。

Mybatis与Spring集成:SqlSessionTemplate

SqlSessionTemplate构造方法

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) { 
   

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    //使用JDK动态代理,创建SqlSessionFactory的代理类的实例 
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { 
    SqlSession.class },new SqlSessionInterceptor());
  }

核心在SqlSessionInterceptor的invoke方法中

  private class SqlSessionInterceptor implements InvocationHandler { 
   
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
   
        //重要:获取sqlSession,具体操作见下方详解
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try { 
   
        //调用真实SqlSession的操作方法
        Object result = method.invoke(sqlSession, args);
        //判断当前sqlSession是否被Spring托管;未被Spring托管则自动commit
        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) { 
   
          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);
        }
      }
    }
  }

SqlSessionUtils.getSqlSession方法

  //从事务管理器中获取sqlSession或创建一个 
  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { 
   

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    //根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder,当sqlSessionFactory创建了sqlSession,就会在事务管理器中添加一对映射:key为sqlSessionFactory,value为SqlSessionHolder,该类保存sqlSession及执行方式 
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    //返回holder中的sqlSession,见下方
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) { 
   
      return session;
    }

    if (LOGGER.isDebugEnabled()) { 
   
      LOGGER.debug("Creating a new SqlSession");
    }
    // 如果holder中返回的sqlSession为空,则创建sqlSession,并注册到holder中去
    session = sessionFactory.openSession(executorType);
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
  private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { 
   
    SqlSession session = null;
    if (holder != null && holder.isSynchronizedWithTransaction()) { 
   
      // 在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用 
      if (holder.getExecutorType() != executorType) { 
   
        throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
      }

      holder.requested();

      if (LOGGER.isDebugEnabled()) { 
   
        LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
      }

      session = holder.getSqlSession();
    }
    return session;
  }

如何将sqlSession注册到holder中去?

  //开启了事务管理时(如springboot常用的的@Transactional),注册session
  //Register session holder if synchronization is active (i.e. a Spring TX is active).
  private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { 
   
    SqlSessionHolder holder;
    ///判断同步是否激活,只要SpringTX被激活,就是true 
    if (TransactionSynchronizationManager.isSynchronizationActive()) { 
   
      //加载环境变量,判断注册的事务管理器是否是Spring管理事务,如果是,则将sqlSession加载进事务管理的本地线程缓存中;否则报错
      Environment environment = sessionFactory.getConfiguration().getEnvironment();
      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { 
   
        if (LOGGER.isDebugEnabled()) { 
   
          LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
        }
        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        // 以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>>中 
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        //设置当前holder和当前事务同步
        holder.setSynchronizedWithTransaction(true);
        //增加引用数 
        holder.requested();
      } else { 
   
        if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { 
   
          if (LOGGER.isDebugEnabled()) { 
   
            LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
          }
        } else { 
   
          throw new TransientDataAccessResourceException(
              "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
        }
      }
    } else { 
   
      if (LOGGER.isDebugEnabled()) { 
   
        LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
      }
    }
}

这里用到了TransactionSynchronizationManager类:

TransactionSynchronizationManager.bindResource:在调用一个需要事务的组件的时候,管理器首先判断当前线程有没有事务,如果没有事务则启动一个事务,并把事务与当前线程绑定。Spring使用TransactionSynchronizationManager的bindResource方法将当前线程与一个事务绑定,采用的方式是ThreadLocal。

    public static void bindResource(Object key, Object value) throws IllegalStateException { 
   
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Assert.notNull(value, "Value must not be null");
        // 此处resources是一个全局变量: 
        //private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
        Map<Object, Object> map = (Map)resources.get();
        if (map == null) { 
   
            map = new HashMap();
            resources.set(map);
        }
        //判断以此sessionFactory为key的Map是否存在。如果存在则此线程已经绑定了 
        Object oldValue = ((Map)map).put(actualKey, value);
        if (oldValue instanceof ResourceHolder && ((ResourceHolder)oldValue).isVoid()) { 
   
            oldValue = null;
        }

        if (oldValue != null) { 
   
            throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
        } else { 
   
            if (logger.isTraceEnabled()) { 
   
                logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]");
            }

        }
    }

可以看出:多线程获取SqlSession时,先从当前线程中获取SqlSessionHolder中的sqlSession;如果为空,再新建sqlSession。所以,多线程公用的是一个SqlSessionTemplate(默认bean的单例注入),但不共用一个SqlSession。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/138199.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 数组转对象2种方法有哪些_js 数组转对象

    数组转对象2种方法有哪些_js 数组转对象constarr=[{label:’男’,value:0},{label:’女’,value:1}]functionf(arr){//写代码//reducereturnarr.reduce((obj,item)=>{obj[item.value]=item.labelreturnobj},{})}constobj=f(arr)console.log(ob…

  • ucosiii应用举例_SATA III接口

    ucosiii应用举例_SATA III接口任务的创建和删除实验uCOSIII是多任务系统,那么肯定要创建任务,创建任务就是将任务控制块、任务堆栈、任务代码等联系在一起,并且初始化任务控制块的相应字段。在UCOSIII中我们通过函数OSTaskCreate();来创建任务,OSTaskCreate();函数原型如下(在os_task.c中有定义)。电泳OSTaskCreate();创建一个任务之后,刚创建的任务就会进入就绪状态,注意!

  • 静态测试和动态测试的区别在哪里?_软件测试中的静态测试

    静态测试和动态测试的区别在哪里?_软件测试中的静态测试1.静态测试静态测试(statictesting)就是不实际运行被测软件,而只是静态地检查程序代码、界面或文档中可能存在的错误的过程。包括对代码测试、界面测试和文档测试三个方面:    对于代码测试,主要测试代码是否符合相应的标准和规范。    对于界面测试,主要测试软件的实际界面与需求中的说明是否相符。    对于文档测试,主要测试用户手册和需求说明是否符合用户的实际需求。…

    2022年10月25日
  • 虚函数详解[通俗易懂]

    虚函数详解[通俗易懂]文章目录一、虚函数实例二、虚函数的实现(内存布局)1、无继承情况2、单继承情况(无虚函数覆盖)3、单继承情况(有虚函数覆盖)4、多重继承情况(无虚函数覆盖)5、多重继承情况(有虚函数覆盖)三、虚函数的相关问题1、构造函数为什么不能定义为虚函数2、析构函数为什么要定义为虚函数?3、如何去验证虚函数表的存在  面向对象的语言有三大特性:继承、封装、多态。虚函数作为多态的实现方式,重要性毋庸置疑。 …

  • C++学习——四种字符串与数据连接的方法

    C++学习——四种字符串与数据连接的方法方法调用很简单,sprintf itoa函数在我的其他博客也有详解,请翻阅查看,话不多说,直接撸代码:#include <iostream>#include <string>#include <cstdlib>#include <sstream>#include <cstring> using namespace std;…

  • Oracle 批量插入(insert all into)

    Oracle 批量插入(insert all into)项目需要用到导入excel表,并解析数据批量插入到oracle数据库中。1)直接解析excel,循环行,拼了sql,executeUpdate。执行一波…咦,这效率很低啊,有多少行数据就执行了多少句sql,基本是一万行已经接近一分钟了。2)每次都仅执行一条sql语句,时间是不是都花在建立连接放开连接balabala的过程上了,用executebatch批量执行sql语句试试。没…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号