`

Spring的事务管理难点剖析(5):联合军种作战的混乱

 
阅读更多
Spring事务管理器的应对


   Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋。像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不太方便;而iBatis则通过模板化技术让你方便地控制SQL,但没有Hibernate那样高的开发效率;自由度最高的当然是直接使用Spring JDBC了,但它也是底层的,灵活的代价是代码的繁复。很难说哪种数据访问技术是最优秀的,只有在某种特定的场景下才能给出答案。所以在一个应用中,往往采用多个数据访问技术:一般是两种,一种采用ORM技术框架,而另一种采用偏JDBC的底层技术,两者珠联璧合,形成联合军种,共同御敌。
   但是,这种联合军种如何应对事务管理的问题呢?我们知道Spring为每种数据访问技术提供了相应的事务管理器,难道需要分别为它们配置对应的事务管理器吗?它们到底是如何协作和工作的呢?这些层出不穷的问题往往压制了开发人员使用联合军种的想法。
   其实,在这个问题上,我们低估了Spring事务管理的能力。如果你采用了一个高端ORM技术(Hibernate、JPA、JDO),同时采用一个JDBC技术(Spring JDBC、iBatis),由于前者的会话(Session)是对后者连接(Connection)的封装,Spring会“足够智能地”在同一个事务线程让前者的会话封装后者的连接。所以,我们只要直接采用前者的事务管理器就可以了。表10-1给出了混合数据访问技术框架所对应的事务管理器。
序    号混合数据访问技术框架事务管理器
1 Hibernate+ Spring JDBC或iBatis org.springframework.orm.hibernate3.HibernateTransactionManager
2 JPA+Spring JDBC或iBatisorg.springframework.orm.jpa.JpaTransactionManager
3 JDO+Spring JDBC或iBatis org.springframework.orm.jdo.JdoTransactionManager


Hibernate+Spring JDBC混合框架的事务管理


   由于一般不会出现同时使用多个ORM框架的情况(如Hibernate+JPA),我们不拟对此命题展开论述,只重点研究ORM框架+JDBC框架的情况。Hibernate+Spring JDBC可能是被使用得最多的组合,本节我们通过实例观察事务管理的运作情况。
package com.baobaotao.mixdao;

…

@Service("userService")
public class UserService extends BaseService {
    @Autowired
    private HibernateTemplate hibernateTemplate;

    @Autowired
    private ScoreService scoreService;

    public void logon(String userName) {
       
        //①通过Hibernate技术访问数据
        System.out.println("before updateLastLogonTime()..");
        updateLastLogonTime(userName);
        System.out.println("end updateLastLogonTime()..");
        
        //②通过JDBC技术访问数据
        System.out.println("before scoreService.addScore()..");
        scoreService.addScore(userName, 20);
        System.out.println("end scoreService.addScore()..");
    }
    
    public void updateLastLogonTime(String userName) {
        User user = hibernateTemplate.get(User.class,userName);
        user.setLastLogonTime(System.currentTimeMillis());
        hibernateTemplate.update(user);

        //③这句很重要,请看下文的分析
        hibernateTemplate.flush();
    }
}

  在①处,使用Hibernate操作数据,而在②处调用ScoreService#addScore(),该方法内部使用Spring JDBC操作数据。
  在③处,我们显式调用了flush()方法,将Session中的缓存同步到数据库中(即马上向数据库发送一条更新记录的SQL语句)。之所以要显式执行flush()方法,原因是在默认情况下,Hibernate对数据的更改只是记录在一级缓存中,要等到事务提交或显式调用flush()方法时才会将一级缓存中的数据同步到数据库中,而提交事务的操作发生在   logon()方法返回前。如果所有针对数据库的更改操作都使用Hibernate,这种数据同步的延迟机制并不会产生任何问题。但是,我们在logon()方法中同时采用了Hibernate和Spring JDBC混合数据访问技术,Spring JDBC无法自动感知Hibernate一级缓存,所以如果不及时调用flush()方法将记录数据更改的一级缓存同步到数据库中,则②处通过Spring JDBC进行数据更改的结果将被Hibernate一级缓存中的更改覆盖掉,因为Hibernate一级缓存要等到logon()方法返回前才同步到数据库!
   ScoreService使用Spring JDBC数据访问技术,其代码如下所示:
package com.baobaotao.mixdao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.apache.commons.dbcp.BasicDataSource;

@Service("scoreService")
public class ScoreService extends BaseService{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void addScore(String userName, int toAdd) {
        String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";
        jdbcTemplate.update(sql, toAdd, userName);
        BasicDataSource basicDataSource = (BasicDataSource) jdbcTemplate.getDataSource();
        //①查看此处数据库激活的连接数量
        System.out.println("[scoreUserService.addScore]激活连接数量:"
                         +basicDataSource.getNumActive());
    }
}

   Spring关键的配置文件代码如下所示:
    …
    <!--①使用Hibernate事务管理器 -->
    <bean id="hiberManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager"
          p:sessionFactory-ref="sessionFactory"/>

    <!--②使UserService及ScoreService的公用方法都拥有事务 -->
    <aop:config proxy-target-class="true">
        <aop:pointcut id="serviceJdbcMethod"
                      expression="within(com.baobaotao.mixdao.BaseService+)"/>
        <aop:advisor pointcut-ref="serviceJdbcMethod"
                     advice-ref="hiberAdvice"/>
    </aop:config>
    <tx:advice id="hiberAdvice" transaction-manager="hiberManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    
</beans>

启动Spring容器,执行UserService#logon()方法,可以查看到如下的执行日志:
引用
before userService.logon()..

①在执行userService.logon()后,Spring开启一个事务
Creating new transaction with name [com.baobaotao.mixdao.UserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
opened session at timestamp: 13009379637
Opened new Session [org.hibernate.impl.SessionImpl@c5f468] for Hibernate transaction

Exposing Hibernate transaction as JDBC transaction [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver]
before userService.updateLastLogonTime()..

②userService.updateLastLogonTime()执行时自动绑定到①处开启的Session中
Found thread-bound Session for HibernateTemplate
loading entity: [com.baobaotao.User#tom]
about to open PreparedStatement (open PreparedStatements: 0, globally: 0)

about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
Not closing pre-bound Hibernate Session after HibernateTemplate
end updateLastLogonTime()..

before scoreService.addScore()..

③scoreService.addScore()执行时绑定到①处开启的Session中,并加入其所对应的事务中
Found thread-bound Session [org.hibernate.impl.SessionImpl@c5f468] for Hibernate
transaction
Participating in existing transaction

SQL update affected 1 rows

④此时数据源只打开了一个连接
[scoreUserService.addScore]激活连接数量:1
end scoreService.addScore()..
Initiating transaction commit

⑤提交Hibernate的事务,它将触发一级缓存到数据库的同步
Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@c5f468]
commit
processing flush-time cascades
dirty checking collections
Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects
Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
listing entities:
com.baobaotao.User{lastLogonTime=1300937963882, score=10, userName=tom, password=123456}
re-enabling autocommit

⑥提效Session底层所绑定的JDBC Connection所对应的事务
committed JDBC Connection
transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
Closing Hibernate Session [org.hibernate.impl.SessionImpl@c5f468] after transaction
Closing Hibernate Session
releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
after userService.logon()..


   仔细观察这段输出日志,在①处UserService#logon()开启一个新的事务。②处的UserService# updateLastLogonTime() 绑定到事务上下文的Session中。③处ScoreService#addScore()方法加入到①处开启的事务上下文中。④处的输出是ScoreService #addScore()方法内部的输出信息,汇报此时数据源激活的连接数为1,这清楚地告诉我们Hibernate和JDBC这两种数据访问技术在同一事务上下文中“共用”一个连接。在⑤处,提交Hibernate事务,接着在⑥处触发调用底层的Connection提交事务。
   从以上的运行结果,我们可以得出这样的结论:使用Hibernate事务管理器后,可以混合使用Hibernate和Spring JDBC数据访问技术,它们将工作于同一事务上下文中。但是使用Spring JDBC访问数据时,Hibernate的一级或二级缓存得不到同步,此外,一级缓存延迟数据同步机制可能会覆盖Spring JDBC数据更改的结果。
   由于混合数据访问技术方案存在“事务同步而缓存不同步”的情况,所以最好用Hibernate进行读写操作,而只用Spring JDBC进行读操作。如用Spring JDBC进行简要列表的查询,而用Hibernate对查询出的数据进行维护。
   如果确实要同时使用Hibernate和Spring JDBC读写数据,则必须充分考虑到Hibernate缓存机制引发的问题:必须整体分析数据维护逻辑,根据需要及时调用Hibernate的flush()方法,以免覆盖Spring JDBC的更改,在Spring JDBC更改数据库时,维护Hibernate的缓存。由于方法调用顺序的不同都可能影响数据的同步性,因此很容易发生问题,这会极大提高数据访问程序的复杂性。所以笔者郑重建议不要同时使用Spring JDBC和Hibernate对数据进行写操作。
   可以将以上结论推广到其他混合数据访问技术的方案中,如Hibernate+iBatis、JPA+Spring JDBC、JDO+Spring JDBC等。

  注:以上内容摘自《Spring 4.x企业应用开发实战》
分享到:
评论
3 楼 liuxiang_eyes 2016-05-31  
现在还真是碰到Hibernate+JPA的情况,这种情况您有研究过吗?我找了很多资料,还是没办法将两者事务统一管理。
2 楼 taya 2012-10-19  
e...这样使用的话,如果别的线程里更新了数据库中的某个值,造成当前hibernate session中缓存不正确的问题,怎么解决?
1 楼 huang_yong 2012-04-14  
Hibernate提供的flush()方法可以提交一级缓存中的数据到数据库中,也就是说,执行该方法,可以立马同步数据库,无需等待业务方法执行完毕后,让Hibernate自动同步到数据库。

这一招确实很猛,保证了非Hibnerate框架获取数据的及时性,而且Spring JDBC可以与Spring Hibernate集成到同一个事务中,对程序员透明化。

感谢作者的分享!

相关推荐

    Spring.3.x企业应用开发实战(完整版).part2

    10.5 联合军种作战的混乱 10.5.1 Spring事务管理器的应对 10.5.2 Hibernate+Spring JDBC混合框架的事务管理 10.6 特殊方法成漏网之鱼 10.6.1 哪些方法不能实施Spring AOP事务 10.6.2 事务增强遗漏实例 10.7 数据连接...

    Spring3.x企业应用开发实战(完整版) part1

    10.5 联合军种作战的混乱 10.5.1 Spring事务管理器的应对 10.5.2 Hibernate+Spring JDBC混合框架的事务管理 10.6 特殊方法成漏网之鱼 10.6.1 哪些方法不能实施Spring AOP事务 10.6.2 事务增强遗漏实例 10.7 数据连接...

    【大数据课设】p105出租车数据可视化分析-大数据-实训大作业.zip

    项目资源包含:可运行源码+数据集+文档 python + numpy, pandas, matplotlib, pyecharts, wordcloud 适用人群:学习不同技术领域的小白或进阶学习者;可作为课程设计、大作业、工程实训或初期项目立项。 数据来源:数据集taxis.csv从网络下载 数据清洗:异常值与缺失值的处理:有一些数据distance(乘车距离)为零而且上下车地点为空,还有些一些数据的payment(支付方式)为空。 数据预处理:将列名更改成中文 标准化与归一化: 数据分析: 数据可视化:

    TypeScript 入门教程

    TypeScript 入门教程

    人脸识别_课堂考勤_OpenCV_服务端系统_1741777828.zip

    人脸识别项目实战

    历届电赛试题及综合测评(真题+模拟题)

    本资源汇总了 历届全国电子设计竞赛(电赛)真题+模拟题,涵盖 电路设计、嵌入式系统、信号处理、自动控制等核心考点,并提供详细解析及综合测评,帮助参赛者高效备赛、查漏补缺、提升实战能力。 适用人群: 适合 准备参加电子设计竞赛的大学生、电赛爱好者、电子信息类相关专业的学生,以及希望提高电子设计和电路分析能力的工程师。 能学到什么: 电赛考察重点:熟悉往届竞赛的命题方向及考核重点。 电路设计与仿真:提升模拟电路、数字电路、单片机等核心技能。 问题分析与解决能力:通过综合测评找到薄弱点并针对性提升。 实战经验:掌握竞赛策略,提高应试效率和设计能力。 阅读建议: 建议先 通读真题,了解题型与解题思路,然后 结合模拟题实战演练,查找不足并通过测评强化练习,逐步提升竞赛能力。

    2024人工智能如何塑造未来产业:AI对各行业组织带来的的变革研究研究报告.pdf

    2024人工智能如何塑造未来产业:AI对各行业组织带来的的变革研究研究报告.pdf

    人脸识别_Golang_SDK_命令行登录_微信小程序应用_1741772240.zip

    人脸识别项目源码实战

    Vulkan原理与实战课程

    给大家分享一套课程——Vulkan原理与实战课程

    SiriYXR_Sokoban11_1741860914.zip

    c语言学习

    海豚鲸鱼数据集 5435张图 正确识别率可达92.6% 可识别:海豚 虎鲸 蜥蜴 海豹 鲨鱼 龟 支持yolov8格式标注

    海豚鲸鱼数据集 5435张图 正确识别率可达92.6% 可识别:海豚 虎鲸 蜥蜴 海豹 鲨鱼 龟 支持yolov8格式标注

    答谢中书书教学设计.docx

    答谢中书书教学设计.docx

    人脸识别_环境搭建_dlib_face_recognitio_1741771308.zip

    人脸识别项目源码实战

    网络技术_Web服务器_C语言_学习交流版_1741863251.zip

    c语言学习

    安卓开发_Gradle配置_React_Native_Meg_1741777287.zip

    人脸识别项目源码实战

    人工智能_深度学习_图像识别_UI界面_项目展示.zip

    人脸识别项目实战

    基于Springboot框架的美发门店管理系统的设计与实现(Java项目编程实战+完整源码+毕设文档+sql文件+学习练手好项目).zip

    本美发门店管理系统有管理员和用户两个角色。用户功能有项目预定管理,产品购买管理,会员充值管理,余额查询管理。管理员功能有个人中心,用户管理,美容项目管理,项目类型管理,项目预定管理,产品库存管理,产品购买管理,产品入库管理,会员卡管理,会员充值管理,余额查询管理,产品类型管理,系统管理等。因而具有一定的实用性。 本站是一个B/S模式系统,采用SSM框架,MYSQL数据库设计开发,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得美发门店管理系统管理工作系统化、规范化。本系统的使用使管理人员从繁重的工作中解脱出来,实现无纸化办公,能够有效的提高美发门店管理系统管理效率。 关键词:美发门店管理系统;SSM框架;MYSQL数据库;Spring Boot 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.1 MYSQL数据库 2 2.2 B/S结构 3 2.3 Spring Boot框架简介 4 3系统分析 4 3.1可行性分析 4 3.1.1技术可行性 4 3.1.2经济可行性 5 3.1.3操作可行性 5 3.2系

    Python实现基于SSA-CNN-GRU麻雀算法优化卷积门控循环单元数据分类预测的详细项目实例(含完整的程序,GUI设计和代码详解)

    内容概要:本文档介绍了基于SSA-CNN-GRU麻雀算法优化卷积门控循环单元数据分类预测的详细项目实例,重点讲述了该项目的背景、目标、挑战与解决方案、技术特点、应用领域等方面的内容。文档详细记录了从项目启动、数据预处理、算法设计(SSA优化CNN-GRU模型)、构建与评估模型到实现美观的GUI界面整个过程,并讨论了防止过拟合的技术如正则化、早停和超参数优化。另外还涵盖了项目扩展的可能性、部署和应用策略、需要注意的地方以及未来改进的方向。全文强调了模型的泛化能力和计算效率,展示了该混合算法模型在实际应用中的优越性能。 适合人群:具备一定的Python编程经验及机器学习基础知识的研究人员和技术人员;对深度学习、智能优化算法及实际应用感兴趣的学者和从业者;寻求提升数据分析和预测准确性的金融分析师、数据科学家等相关专业人士。 使用场景及目标:本文档非常适合用作学习和参考资料,以掌握如何将SSA、CNN与GRU三种先进技术结合起来进行复杂的分类和预测问题求解。具体应用场景包括但不限于以下几个方面:金融领域——股票价格预测;医疗保健领域——辅助诊断;工业制造——预防性维护;智能家居——个性化服务;以及其他涉及到时序数据分析和多模态数据处理的场合。文档既包含了理论知识又提供了完整的源代码示例,可以帮助读者理解算法原理并通过实践中加深对其的认识。 其他说明:该项目不仅仅是关于算法的设计实现,更是有关于系统的整体架构规划以及工程上的考量,比如环境准备(确保环境洁净、必要包的安装等)、数据准备、GPU配置支持等等。同时文中给出了详细的代码片段,方便开发者理解和复现实验成果。值得注意的是,虽然文中提供了一套通用解决方案,但在真实场景下还需要针对性的调整参数或修改网络结构来达到最好的性能效果。此外,对于追求更高的预测精度或解决更大规模的问题,作者建议进一步探索深度强化学习等高级技术和多任务学习策略,并且考虑使用增量学习让模型能够适应新数据而不必重新训练整个模型。最后提到安全性和隐私保护也是项目实施过程中的重要因素,要妥善保管用户的敏感信息并且做到合法合规地收集和使用数据。

    人脸识别_T形分布_Gabor变换_特征提取_增强鲁棒性_1741777397.zip

    人脸识别项目实战

    13005463562_FaceWeb_1741771809.zip

    人脸识别项目实战

Global site tag (gtag.js) - Google Analytics