Spring 事务管理的为我们的提供了解决方案。

当我们采用:ORM 技术框架+ 偏 JDBC 的底层技术如何应对事务管理的问题呢? 我们知道 Spring 为每种数据访问技术提供了相应的事务管理器,难道需要分别为它们配置对应的事务管理器吗?它们到底是如何协作,如何工作的呢?

解决方案

当我们采用了一个高端 ORM 技术(Hibernate,JPA,JDO),同时采用一个 JDBC 技术(Spring JDBC,MyBatis),由于前者的会话(Session)是对后者连接(Connection)的封装,Spring 会“足够智能地”在同一个事务线程让前者的会话封装后者的连接。所以,我们只要直接采用前者的事务管理器就可以了。

我们列举下混合数据访问技术所对应的事务管理器:

这里写图片描述

示例:Hibernate + Spring JDBC

由于一般不会出现同时使用多个 ORM 框架的情况(如 Hibernate + JPA),我们不拟对此命题展开论述,只重点研究 ORM 框架 + JDBC 框架的情况。

Hibernate + Spring JDBC 可能是被使用得最多的组合,我们通过实例来观察事物的运行情况。

User 使用了注解声明的实体类

import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.Id;
import java.io.Serializable;

@Entity
@Table(name="T_USER")
public class User implements Serializable{
@Id
@Column(name = "USER_NAME")
private String userName;
private String password;
private int score;

@Column(name = "LAST_LOGON_TIME")
private long lastLogonTime = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UserService 使用 Hibernate 数据访问技术

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.apache.commons.dbcp.BasicDataSource;
import user.User;

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

@Autowired
private ScoreService scoreService;

public void logon(String userName) {
System.out.println("logon method...");
updateLastLogonTime(userName); //①使用Hibernate数据访问技术
scoreService.addScore(userName, 20); //②使用Spring JDBC数据访问技术
}

public void updateLastLogonTime(String userName) {
System.out.println("updateLastLogonTime...");
User user = hibernateTemplate.get(User.class,userName);
user.setLastLogonTime(System.currentTimeMillis());
hibernateTemplate.flush(); //③请看下文的分析
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在①处,使用 Hibernate 操作数据,而在②处调用 ScoreService#addScore(),该方法内部使用 Spring JDBC 操作数据。

在③处,我们显式调用了 flush() 方法,将 Session 中的缓存同步到数据库中,这个操作将即时向数据库发送一条更新记录的 SQL 语句。

之所以要在此显式执行 flush() 方法,原因是:默认情况下,Hibernate 要在事务提交时才将数据的更改同步到数据库中,而事务提交发生在 logon() 方法返回前。

如果所有针对数据库的更改都使用 Hibernate,这种数据同步延迟的机制不会产生任何问题。但是,我们在 logon() 方法中同时采用了 Hibernate 和 Spring JDBC 混合数据访问技术。

Spring JDBC 无法自动感知 Hibernate 一级缓存,所以如果不及时调用 flush() 方法将数据更改同步到数据库,则②处通过 Spring JDBC 进行数据更改的结果将被 Hibernate 一级缓存中的更改覆盖掉,因为,一级缓存在 logon() 方法返回前才同步到数据库!

ScoreService :使用 Spring JDBC 数据访问技术

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

@Service("scoreUserService")
public class ScoreService extends BaseService{
@Autowired
private JdbcTemplate jdbcTemplate;
public void addScore(String userName, www.thd178.com int toAdd) {
System.out.println("addScore...");
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("激活连接数量:"+basicDataSource.getNumActive());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
关键配置文件

<!-- 使用Hibernate事务管理器 -->
<bean id="hiberManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>

<!-- 对所有继承BaseService类的公用方法实施事务增强 -->
<aop:config proxy-target-class="true">
<aop:pointcut id="serviceJdbcMethod"
expression="within(com.artisan.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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
日志:

21:37:57,062 (AbstractPlatformTransactionManager.java:365) - Creating new transaction
with name [com.artisan.UserService.logon]:www.hjd157.com PROPAGATION_REQUIRED,ISOLATION_DEFAULT

21:37:57,093 (SessionImpl.java:220) - opened session at timestamp: 12666407370

21:37:57,093 (HibernateTransactionManager.java:493) - Opened new Session
[[email protected]] for Hibernate transaction ①

21:37:57,093 (HibernateTransactionManager.java:504) - Preparing JDBC Connection
of Hibernate Session [[email protected]]

21:37:57,109 (JDBCTransaction.java:54) - begin

logon method...
updateLastLogonTime...

21:37:57,109 (AbstractBatcher.java:401) - www.wanuoyl88.cn select user0_.USER_NAME as USER1_0_0_,
user0_.LAST_LOGON_TIME as LAST2_0_0_, user0_.password as password0_0_,
user0_.score as score0_0_ from T_USER user0_ where user0_.USER_NAME=?

Hibernate: select user0_.USER_NAME as USER1_0_0_,
user0_.LAST_LOGON_TIME as LAST2_0_0_, user0_.www.lgzxyl.com password as password0_0_,
user0_.score as score0_0_ from T_USER user0_ where user0_.USER_NAME=?

21:37:57,187 (HibernateTemplate.java:422) - Not closing pre-bound
Hibernate Session after HibernateTemplate

21:37:57,187 (HibernateTemplate.java:397) - Found thread-bound Session
for HibernateTemplate

Hibernate: update T_USER set LAST_LOGON_TIME=?, password=?, score=? where USER_NAME=?

2017-09-26 21:37:57,203 DEBUG [main] (AbstractPlatformTransactionManager.java:470)
- Participating in existing transaction ②
addScore...

2017-09-26 21:37:57,203 DEBUG [main] (JdbcTemplate.java:785)
- Executing prepared SQL update

2017-09-26 21:37:57,203 DEBUG [main] (JdbcTemplate.java:569)
- Executing prepared SQL statement
[UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?]

2017-09-26 21:37:57,203 DEBUG [main] (JdbcTemplate.java:794)
- SQL update affected 1 rows

激活连接数量:1 ③
2017-09-26 21:37:57,203 DEBUG [main] (AbstractPlatformTransactionManager.java:752)
- Initiating transaction commit
2017-09-26 21:37:57,203 DEBUG [main] (HibernateTransactionManager.java:652)
- Committing Hibernate transaction on Session
[[email protected]] ④

2017-09-26 21:37:57,203 DEBUG [main] (JDBCTransaction.java:10www.gouyifl.cn/3) - commit ⑤
在①处 UserService#logon() 开启一个新的事务,
在②处 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 www.tianhengyl1.com JDBC 的更改,在 Spring JDBC 更改数据库时,维护 Hibernate 的缓存。

可以将以上结论推广到其它混合数据访问技术的方案中,如 Hibernate+MyBatis,JPA+Spring JDBC,JDO+Spring JDBC 等

时间: 2024-10-16 02:15:46

Spring 事务管理的为我们的提供了解决方案。的相关文章

Spring 事务管理高级应用难点剖析--转

第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN&en=utf&hpp=20&dws=cndw&lo=zh 概述 Spring 最成功,最吸引人的地方莫过于轻量级的声明式事务管理,仅此一点,它就宣告了重量级 EJB 容器的覆灭.Spring 声明式事务管理将开发者从繁复的事务管理代码中解脱出来,专注于业务逻辑的开发上,这是一件

Spring 事务管理详情介绍

一.事务管理介绍 事务是现代数据库理论中的核心概念之一,是逻辑上的一组操作,这组操作要么全都成功,要么全都失败.如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务.当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交.由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态.分别是:提交事务(调用commit()方法).回滚事务(失败提交-调用rollBack()方法).有如下优点: 1.为不同的事务API提供一致的编程模型,如JTA,JD

Spring第三天——JdbcTemplate和spring事务管理

大致内容: aspectJ的aop操作(基于注解,对比day02配置操作)(会用) *jdbcTemplate操作(实现CRUD) *spring配置连接池 *spring事务管理 一.AspectJ的基于注解的AOP操作 (day02的配置回顾,略显麻烦,配置稍多) 建立项目记得导入day02操作aop的那些包(如果是复制项目一定要修改项目的context name) 再把配置文件拿过来 <?xml version="1.0" encoding="UTF-8"

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

Spring事务管理

写这篇博客之前我首先读了<spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

Spring事务管理--多个ORM框架在使用时的情况分析

公司的项目已经接近尾声了,总结一下项目中用到的技术,我发现项目中的有些东西还是挺模糊的,只是知道这么用就行了.并不清楚其中的原理.由于公司的项目比较老,是7年前的一个项目了,中间一直有人在维护,也是在这个过程中不断融入了新的东西,比如就项目的持久化这块来说,就用了ibatis.mybatis.hibernate.spring JDBC四种混合的框架.究其原因只能说是历史遗留问题,就不做过多的解释了.但是这么多持久化的框架如何协同工作的,尤其是事务的控制,一个系统中使用如此多的持久化框架是,他们是

详细介绍Spring事务管理

在学习spring事务管理时,我忍不住要问,spring为什么进行事务管理,spring怎么进行的事务管理?首先,为什么要进行事务,接下来说说spring是怎样进行事务管理的. 我们都知道spring提供两种管理事务的方式,一种是声明式事务,一种是编程式事务. Spring的声明式事务管理,基于Spring的AOP,不再需要不停地写commit,rollback,(但Spring仍然没有放弃编程式的事务管理策略). Spring的编程式事务管理,为我们提供了一个TransactionTempla

Spring事务管理----------整合学习版

作者:学无先后 达者为先 Spring提供了一流的事务管理.在Spring中可以支持声明式事务和编程式事务. 一  spring简介 1 Spring的事务       事务管理在应用程序中起着至关重要的作用:它是一系列任务的组成工作单元,在这个工作单元中,所有的任务必须同时执行.它们只有二种可能执行结果,要么所有任务全部执行成功,要么所有任务全部执行失败.     Spring中提供了丰富的事务管理功能,它们超过了EJB并且和EJB一样支持声明式事务,重要的是Spring提供了一致的事务管理,

spring事务管理——编程式事务、声明式事务

本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带有 512MB 内存(