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)


相关推荐

  • Base64转换成图片[通俗易懂]

    Base64转换成图片[通俗易懂]2019独角兽企业重金招聘Python工程师标准>>>…

  • cgal使用_cgal库

    cgal使用_cgal库CGAL5.3-手动:在Windows上使用CGAL(使用VisualC++)

    2022年10月23日
  • HTML中空格_如何去掉word中的空格

    HTML中空格_如何去掉word中的空格1. 空格符大  家  好!显示效果为:大家好!2.p标签使用空两格百度地图API功能百度地图API功能效果如下:3.span标签空格百度地图API功能百度地图API功能效果如下:4.word-spacing属性,Happynewyear!效果:![在这里插入图片描述](https://img-blog….

  • 最长上升子序列(n*log n)

    最长上升子序列(n*log n)转载于:http://www.cnblogs.com/dongsheng/archive/2013/01/25/2876323.htmlNYOJ-214单调递增子序列(二)单调递增子序列(二)时间限制:1000 ms |          内存限制:65535 KB难度:4 描述给定一整型数列{a1,a2…,an}(0&lt;n&lt;=100000),找出单…

  • [中国大学Mooc]Web前端测试题

    [中国大学Mooc]Web前端测试题1、web前端开发中“web”指的是: C web系统2、以下哪个概念或者哪个功能属于“前端”: A、web系统中以网页的形式为用户提供的部分,用户能接触到的部分;3、以下关于网页源文件的叙述不正确的是: B、网页源文件客户端是看不见的;4、以下关于前端技术标准叙述不正确的是: B、技术标准是由W3School组织提供的;5、以下…

  • phpstorm2022 激活码[最新免费获取]

    (phpstorm2022 激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~0V…

发表回复

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

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