SqlSessionTemplate探究

SqlSessionTemplate探究问题就是:无论是多个dao使用一个SqlSessionTemplate,还是一个dao使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个sqlSession,当多个web线程调用同一个dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,如何保证线程安全,关键就在于代理:(1)首先,通过如下代码创建代理类

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

 问题就是:无论是多个dao使用一个SqlSessionTemplate,还是一个dao使用一个SqlSessionTemplateSqlSessionTemplate都是对应一个sqlSession,当多个web线程调用同一个dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,如何保证线程安全,关键就在于代理:

1)首先,通过如下代码创建代理类,表示创建SqlSessionFactory的代理类的实例,该代理类实现SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptorinvoke方法

1. this.sqlSessionProxy = (SqlSession) newProxyInstance( 

2.         SqlSessionFactory.class.getClassLoader(), 

3.         new Class[] { SqlSession.class }, 

4.         new SqlSessionInterceptor()); 

2)所以关键之处转移到invoke方法中,代码如下,该类的注释是代理将Mybatis的方法调用导向从Spring的事务管理器获取的合适的SqlSession,说明虽然都是调用同样一个SqlSession接口,但是实际执行sqlsqlSession会有所不同。

1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 

2.       //调用SqlSessionUtilsgetSqlSession方法从Spring的事务管理器获取合适的SqlSession 

3.    final SqlSession sqlSession = getSqlSession( 

4.           SqlSessionTemplate.this.sqlSessionFactory, 

5.           SqlSessionTemplate.this.executorType, 

6.           SqlSessionTemplate.this.exceptionTranslator); 

7.       try { 

8.   //通过sqlSession对象调用该方法 

9.         Object result = method.invoke(sqlSession, args); 

10.         //判断sqlSession是否被Spring事务管理,也就是sqlSession被放在Spring事务管理的本地线程缓存中。如果不是,则需要自己提交。如果是,则Spring通过代理机制,进行提交和回滚 

11.         if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { 

12.           // force commit even on non-dirty sessions because some databases require 

13.           // a commit/rollback before calling close() 

14.           sqlSession.commit(true); 

15.         } 

16.   //返回方法的调用结果 

17.         return result; 

18.       } catch (Throwable t) { 

19.   //如果出现异常,则利用异常转换器将Mybatis的异常转为SpringDataAccessException 

20.         Throwable unwrapped = unwrapThrowable(t); 

21.         if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException)     { 

22.           Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); 

23.           if (translated != null) { 

24.             unwrapped = translated; 

25.           } 

26.         } 

27.         throw unwrapped; 

28.       } finally { 

29.   //方法调用完毕后,关闭sqlSession连接 

30.         closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); 

31.       } 

32.     } 


其中,核心有两部分:

3)如何从Spring的事务管理器中获得合适的sqlSession,从而保证线程安全,很明显所有dao的多个线程不是使用同一个sqlSession,不然其中一个closeSqlSession,其他怎么用。

该方法的注释:从Spring事务管理器中得到一个SqlSession,如果需要创建一个新的。首先努力从当前事务之外得到一个SqlSession,如果没有就创造一个新的。然后,如果Spring TX被激活,也就是事务被打开,且事务管理器是SpringManagedTransactionFactory时,将得到的SqlSession同当前事务同步,下面是该函数的核心代码

1. public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {     

2.     //根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder,当sqlSessionFactory创建了sqlSession,就会在事务管理器中添加一对映射:keysqlSessionFactoryvalueSqlSessionHolder,该类保存sqlSession及执行方式 

3.     SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory); 

4.  //如果holder不为空,且和当前事务同步 

5.     if (holder != null && holder.isSynchronizedWithTransaction()) { 

6.       //hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常,也就是说在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用 

7.       if (holder.getExecutorType() != executorType) { 

8.         throw new TransientDataAccessResourceException(“Cannot change the ExecutorType when there is an existing transaction”); 

9.       } 

10.       //增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加 

11.       holder.requested(); 

12.    //返回sqlSession 

13.       return holder.getSqlSession(); 

14.     } 

15.  //如果找不到,则根据执行类型构造一个新的sqlSession 

16.     SqlSession session = sessionFactory.openSession(executorType); 

17.  //判断同步是否激活,只要SpringTX被激活,就是true 

18.     if (isSynchronizationActive()) { 

19.    //加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务 

20.       Environment environment = sessionFactory.getConfiguration().getEnvironment(); 

21.       if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { 

22.   //如果是,则将sqlSession加载进事务管理的本地线程缓存中 

23.         holder = new SqlSessionHolder(session, executorType, exceptionTranslator); 

24.   //sessionFactorykeyhodlervalue,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources 

25.         bindResource(sessionFactory, holder); 

26.   //holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations 

27.         registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); 

28.         //设置当前holder和当前事务同步 

29.   holder.setSynchronizedWithTransaction(true); 

30.   //增加引用数 

31.         holder.requested(); 

32.       } else { 

33.         if (getResource(environment.getDataSource()) == null) { 

34.         } else { 

35.           throw new TransientDataAccessResourceException( 

36.               “SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization”); 

37.         } 

38.       } 

39.     } else { 

40.     } 

41.     return session; 

42.   } 

 

上述代码中可以看出,只有在一个线程的一个事务中,由同一个sqlSessionFactory创建的执行类型相同的sqlSession才会被复用,其他情况下都是创建新的sqlSession。试想一下,即使只有一个SqlSessionTemplate供所有dao使用,所有地方使用的都是同以个sqlSessionFactory,但是由于是不同线程,所以得到的不是同一个sqlSession,因此不会出现线程安全问题。好处是同一个线程同一个事务中sqlSession会被复用,不会每执行一个sql请求,都创建一个SqlSession,这样很浪费资源,因为SqlSession相当于一次数据库连接。

4)如何关闭sqlSession连接,主要代码如下

1. public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) { 

2.  //其实下面就是判断session是否被Spring事务管理,如果管理就会得到holder  

3.     SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory); 

4.     if ((holder != null) && (holder.getSqlSession() == session)) { 

5.    //这里释放的作用,不是关闭,只是减少一下引用数,因为后面可能会被复用 

6.       holder.released(); 

7.     } else { 

8.    //如果不是被spring管理,那么就不会被Spring去关闭回收,就需要自己close 

9.       session.close(); 

10.     } 

11.   } 

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

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

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

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

(0)


相关推荐

  • 组合模式 Composite[通俗易懂]

    组合模式 Composite[通俗易懂]组合模式 Composite动机模式定义实例结构要点总结笔记动机在软件某些情况下,客户代码过多地依赖于对象容器复杂的内部实现结构,对象内部实现结构(而非抽象接口)地变化将引起客户代码地频繁变化.带来代码地维护性,扩展性等弊端如何将”客户代码与复杂地对象容器结构”解耦?让对象容器自己来实现自身地复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂地对象容器模式定义将对象组合成树形结构以表示”整体-部分”地层次结构.Composite使得用户对单个对象和组合对象地使用具有一致性(稳定)实例树形

  • phptorm 激活码【在线注册码/序列号/破解码】

    phptorm 激活码【在线注册码/序列号/破解码】,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • pycharm安装后无法运行_pycharm代码运行不了

    pycharm安装后无法运行_pycharm代码运行不了问题:pycharm安装后不能执行python脚本的问题原因:pycharm没有设置解析器解决方法:打开pycharm->File->Settings->ProjectInterpreter->设置未你的python路径,我的是:C:\Python27\python.exe,你们根据各自python安装路径修改一下即可

  • Delphi中谨慎使用QuotedStr、QuotedString、DequotedString相关的函数

    Delphi中谨慎使用QuotedStr、QuotedString、DequotedString相关的函数//以下测试代码vars,ss:string;begin//1.添加定界符(内容中含定界符的要转义)ss:=QuotedStr(s);//单引号ss:=s.QuotedString(””);//单引号//2.添加指定的定界符(内容中含定界符的要转义)ss:=AnsiQuotedStr(s,'”‘);//双引号ss:=s.QuotedString(‘”‘);//双引号//3.去掉定界符(内容中含连续两个定界符的要反转义)…

    2022年10月18日
  • 小米笔记本、小米游戏本重装原装出厂镜像教程-有百度盘的提取码

    小米笔记本、小米游戏本重装原装出厂镜像教程-有百度盘的提取码转:【新的干货儿】小米笔记本、小米游戏本重装原装出厂镜像教程原文转自:http://bbs.xiaomi.cn/t-36117135作者主页:http://bbs.xiaomi.cn/u-detail-426023643转载仅供学习,感谢原作者分享。【重装前须知】有百度盘的提取码1.本教程完全为个人观点,不代表官方,仅供参考。2.重装系统需谨慎,由此带来的任何问题与本人无…

  • OpenResty 最佳实践学习–实战演习笔记(3)

    本篇总结来自 OpenResty(Nginx+Lua)开发入门 ,基本的代码没有改动,主要是自己实际动手操作,测试 Nginx Lua API !我们需要接收请求、处理并输出响应。而对于请求我们需要获取如请求参数、请求头、Body体等信息;而对于处理就是调用相应的Lua代码即可;输出响应需要进行响应状态码、响应头和响应内容体的输出。因此我们从如上几个点出发即可。接收请求我自己进行测试,在我安装的

发表回复

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

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