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。

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • C语言编写一个计算器(附全源代码)「建议收藏」

    C语言编写一个计算器(附全源代码)「建议收藏」这个计算器其实是我老师布置的一个c语言大作业,捉摸着搞了那么久的东西不能浪费了吧,于是我分享下我的代码和大概思路给个关注点个赞,后续我会分享更多我们学生党的作业问题白嫖党们先看代码,我就先上上全代码,干!(一)软件环境:Devc++我用的这个软件哈,个人感觉这里功能简单,特别容易上手。看图说话,是不是很简单嘛,又不复杂。(二)设计方案根据自学所得栈进行数据和符号的存储再输出,先设立单独的数据栈符号栈,我们以top=-1为标准,判断其是否为空栈,当然也用到了学过的struct来构建栈,先把字符存

    2022年6月3日
    237
  • bootstrap fileinput api

    bootstrap fileinput apiBootstrapFileInput中文API整理

    2022年5月24日
    36
  • windows copy命令详解

    windows copy命令详解将一份或多份文件复制到另一个位置 COPY D V N Y Y Z A B source A B source A B destination A B source 指定要复制的文件 A 表示一个 ASCII 文本文

    2025年7月31日
    5
  • Vue生命周期钩子(三)「建议收藏」

    Vue生命周期钩子(三)「建议收藏」Vue生命周期图Vue中共有11个生命周期函数,本文只说明8个生命周期钩子beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好data和methods属性created:实例已经在内存中创建完毕,此时 data 和 methods 已经创建完毕,此时还没有开始编译模板beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中…

    2022年6月13日
    29
  • 联合概率,边际概率,条件概率的区别_非条件概率和边际概率

    联合概率,边际概率,条件概率的区别_非条件概率和边际概率一时忘了联合概率、边际概率、条件概率是怎么回事,回头看看。某离散分布:联合概率、边际概率、条件概率的关系:其中,Pr(X=x,Y=y)为“XY的联合概率”;Pr(X=x)为“X的边际概率”;Pr(X=x|Y=y)为“X基于Y的条件概率”;Pr(Y=y)为“Y的边际概率”;从上式子中可以看到:Pr(X=x,Y=y)=Pr(X=x|Y=y)*Pr(Y=y)

    2022年10月18日
    3
  • vmware15最新激活码2021_通用破解码

    vmware15最新激活码2021_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    188

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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