第20章 使用Spring进行事务管理

20.2 声明式事务管理

20.2.2 XML元数据驱动的声明式事务

2. 使用“一站式”的TransactionProxyFactoryBean

TransactionProxyFactoryBean是专门面向事务管理的ProxyFactoryBean,它直接将TransactionInterceptor纳入自身进行管理。 使用TransactionProxyFactoryBean代替ProxyFactoryBean进行声明式事务管理,不需要单独声明TransactionInterceptor的bean定义,有关事务的元数据、事务管理器等信息,全都通过TransactionProxyFactoryBean的bean定义指定就可以。

这样,同样针对QuoteService的声明式事务管理,使用TransactionProxyFactoryBean后的样子如下方代码清单所示。

<beans><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="url" value="jdbc:mysql://1oca1host/dbName"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="username" value="..."/><property name="password" value="..."/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><bean id="quoteServiceTarget"class="...QuoteService"><property name="jdbcTemplate" ref="jdbcTemplate"/></bean><bean id="quoteService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="target" ref="quoteServiceTarget"/><property name="proxyInterfaces" value="...IQuoteService"/><property name="transactionManager" ref="transactionManager"/><property name="transactionAttributes"><props><propkey="getQuate*">PROPAGATION_SUPPORTS,readOnly,timeout_10</prop><propkeys"saveQuote">PROPAGATION_REQUIRED</prop><propkey="updateQuote">PROPAGATION_REQUIRED</prop><propkey="deleteQuote">PROPAGATION_REQUIRED</prop></props></property></bean><bean id="client" class="...QuoteSerivceClient"><property name="quoteService" ref="quoteService"/></bean>
</beans>

现在,TransactionProxyFactoryBean集ProxyFactoryBean、TransactionInterceptor功能于一身,一心一意地为声明式事务管理做贡献了。

不过,我们也看到了,针对TransactionProxyFactoryBean的bean定义看起来不是那么苗条,如果每个需要声明式事务的业务对象都来这么一下子,那么配置量可着实不轻松。所以,通常情况下,我们会使用bean定义模板的方式,来简化使用TransactionProxyFactoryBean进行声明式事务的配置,如下方代码清单所示。

<bean id="txProxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"><property name="proxyInterfaces" value="...IQuoteService"/><property name="transactionManager" ref="transactionManager"/><property name="transactionAttributes"><props><propkey="getQuate*">PROPAGATION_SUPPORTS,readOnly,timeout_10</prop><propkey"saveQuote">PROPAGATTON__REQUIRED</prop><propkey="updateQuote">PROPAGATION_REQUIRED</prop><propkey="deleteQuote">PROPAGATION_REQUIRED</prop></props></property>
</bean><bean id="quoteService" parent="txProxyFactoryBean"><property name="target" ref="quoteServiceTarget"/>
</bean><bean id="quoteService2" parent="txProxyFactoryBean"><property name="target" ref="otherQuoteServiceTarget"/>
</bean>
...

将共有的一些属性提取到txProxyFactoryBean的bean定义模板中,就可以减少每次配置单独业务对象对应的bean定义的工作量。

相对于直接使用ProxyFactoryBean和TransactionInterceptor,使用TransactionProxyFactoryBean可以将声明式事务相关的关注点集中起来,一定程度上减少了配置的工作量。不过话又说回来了,如果应用程序中仅有少量的业务对象需要配置声明式事务,那么配置的工作量还算说的过去,一旦需要声明式事务的业务对象数量增加,采用这种近乎“手工作坊式”的配置方式就会"拖后腿”了。这时,我们自然会想到AOP中的自动代理机制,而下面正是针对如何使用自动代理对声明式事务进行管理的内容。

3. 使用BeanNameAutoProxyCreator

使用BeanNameAutoProxyCreator进行声明式事务管理进一步地简化了配置的工作。当所有的声明式事务相关装备一次到位之后,要为新的业务对象添加声明式事务支持,唯一要做的就是,在为该业务对象添加bean定义的时候,同时将它的beanName添加到BeanNameAutoProxyCreator管理的beanNames列表中。

使用BeanNameAutoProxyCreator为业务对象提供声明式事务支持,通常配置如下方代码清单所示。

<beans><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="url" value="jdbc:mysql://localhost/databaseName"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="username" value="..."/><property name="password" value="..."/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSouiceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"><property name="transactionManager" ref="transactionManager"/><property name="transactionAttributeSource"><value>              org.spring21.package.IQuoteService.getQuate*=PROPAGATION_SUPPORTS,readOnly,timeout_20
org.spring21.package.IQuoteService.saveQuote=PROPAGATION_REQUIRED
org.spring21.package.IQuoteService.updateQuote=PROPAGATION_REQUIRED
org.spring21.package.IQuoteService.de1eteQuote=PROPAGATION_REQUIRED</value></property></bean><bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"><property name="interceptorNames"><list><value>transactionInterceptor</va1ue></list></property><property name="beanNames"><list><idref bean="quoteService"/>...</list></property></bean><bean id="quoteService" class="...QuoteService"><property name="jdbcTemplate" ref="jdbcTemplate"/></bean><bean id="client" class="...QuoteSerivceClient"><property name="quoteService" ref="quoteService"/></bean>
</beans>

现在,我们只需要正常地向IoC容器的配置文件中增加相应的业务对象bean定义。BeanNameAutoProxyCreator将根据TransactionInterceptor提供的事务管理功能,为添加到它的beanNames列表的所有业务对象自动添加事务支持(当然,本质上是为其生成动态代理对象)。

无论应用中业务对象数量多少,使用BeanNameAutoProxyCreator都可以很便捷地处理这些业务对象的声明式事务需求。不过,可能在实际的开发过程中,我们依然会感觉使用BeanNaneAutoProxyCreator有其不够便捷之处。好消息就是,如果我们的应用程序可以,或者已经升级到Spring2.x,那么,使用基于XSD的配置方式吧!

4. 使用Spring2.x的声明事务配置方式

Spring2.x后提供的基于XMLSchema的配置方式,专门为事务管理提供了一个单独的命名空间用于简化配置,结合新的tx命名空间,现在的声明式事务管理看起来要清晰许多(见下方代码清单)。

<beans xmlns:="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jee="http://www.springframework.org/schema/jee"
xm1ns:lang="http://www.springframework.org/schema/lang"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http:1/www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/Bchema/tx
http://www.springframework.org/echema/tx/spring-tx-2.0.xsd"><aop:config><aop:pointcut id="txServices" expression="execution(*cn.spring21.unveilspring.IQuoteService.*(..))"/><aop:advisor pointcut-ref="txServices" advice-ref="txAdvice"/></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attrlbutes><tx:method name="getQuate*" propagation="SUPPORTS" read-only="true" timeout="20"/><tx:method name="saveQuote"><tx:method name="updateQuote"/><tx:method name="deleteQuote"/></tx:attributes></tx:advice><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="url" value="jdbc:mysql://localhost/databaseName"/><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="username" value="..."/><property name="password" value="..."/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSouiceTransactionManager"><property name="dataSource" ref="dataSource"/></bean>  <bean id="quoteService" class="...QuoteService"><property name="jdbcTemplate" ref="jdbcTemplate"/></bean><bean id="client" class="...QuoteSerivceClient"><property name="quoteService" ref="quoteService"/></bean>
</beans>

<tx:advice>是专门为声明事务Advice而设置的配置元素,底层当然还是我们的TransactionInterceptor,只是披一件新衣裳而已。

<tx:advice>的transaction-manager指定了它要使用的事务管理器是哪一个。如果容器中事务管理器的beanName恰好就是transactionManager,那么可以不明确指定。

<tx:advice>内部由<tx:attributes>提供声明式事务所需要的元数据映射信息,每条映射信息对应一一个<tx:method/>元素声明。<tx:method/>只有name属性是必须指定的,其他的属性代表事务定义的其他内容,比如propagation用于指定传播行为,isolation用于指定隔离度,timeout用于指定事务的超时时间等。如果不明确指定的话,将采用DefaultTransactionDefinition的设置内容。

表20-1是<tx:method/>可用的属性的详细列表。

通过<tx:advice>指定的事务信息,需要有SpringAOP的支持才能织入到具体的业务对象,所以,剩下的工作实际上是AOP的配置了,如下所示:

<aop:config><aop:pointcut id="txServices" expression="execution(*cn.spring21.unveilspring.IQuoteService.*(..))"/><aop:advisor pointcut-ref="txServices" advice-ref="txAdvice"/>
</aop:config>

在SpringAOP部分已经说过,<aop:config>底层也是依赖于自动代理机制。所以,我一直强调,新的基于XSD的配置方式,只是换了一身简洁明快的外衣,而我想让大家看到的,则是外衣里面的东西。

注意:更多使用tx:advice的内容可以参照Spring2.x之后的参考文档,其中有更多详细内容,比如配置多个tx:advice以区分不同的事务需求等内容。

20.2.3 注解元数据驱动的声明式事务

随着Java5(Tiger)的发布,注解越来越受到开发人员的关注和喜爱,如果你的应用程序构建在Java5或者更高版本的虚拟机上的话,那么恭喜你,现在你也可以使用Spring提供的基于注解的声明式事务管理了。

注解元数据驱动的声明式事务管理的基本原理是,将对应业务方法的事务元数据,直接通过注解标注到业务方法或者业务方法所在的对象上,然后在业务方法执行期间,通过反射读取标注在该业务方法上的注解所包含的元数据信息,最终将根据读取的信息为业务方法构建事务管理的支持。

Spring定义了org.springframework.transaction.annotation.Transactional用于标注业务方法所对应的事务元数据信息。通过Transactional,可以指定与<tx:method/>几乎相同的信息。当然,现在不用指定方法名称了,因为Transactional直接标注到了业务方法或者业务方法所在的对象定义上。通过查看Transactional的定义(见下方代码清单),我们可以获取所有可以指定的事务定义内容。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default{};String[] rollbackForClassName() default{};Class<? extends Throwable>[] noRollbackFor() default{};String[] noRollbackForClassName() default{};
}

要为QuoteService添加基于注解的声明式事务管理,需要为其添加Transactional以标注必要的事务管理信息,如下方代码清单所示。

@Transactional
public class QuoteService implements IQuoteService {private JdbcTemplate jdbcTemplate;@Transactional(propagation=Propagation.SUPPORTS,reaaOnly=true,timeout=20)public Quote getQuate() {return(Quote)getJdbcTemplate().queryForObject("SELECT * FROM fx_quote where quote_id=2", new RowMapper() {public Object mapRow(ResultSet rs, int row) throws SQLException {Quote quote = new Quote();//...return quote;}});}@Transactional(propagation=Propagation,SUPPORTS,readOnly=true,timeout=20)public Quote getQuateByDateTime(DateTime dateTime) {throw new NotImplementedException();}public void saveQuote(Quote quote) {throw new NotImp1ementedException();}public void updateQuote(Quote quote) {throw new NotImplementedException();}public void deleteQuote(Quotequote) {throw new NotImplementedException();}public JdbcTemplate getJdbcTemplate() {return jdbcTemplate;}public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}
}

如果将@Transactional标注为对象级别的话,对象中的方法将“继承”该对象级别上的@Transactional的事务管理元数据信息。如果某个方法有特殊的事务管理需求,可以在方法级别添加更加详细的@Transactional设定,比如getQuote*()方法。通过将相同的事务管理行为提取到对象级别的@Transactional,可以有效地减少标注的数量。如果不为@Transactional指定自定义的一些设定,它也会像<tx:method/>那样采用与DefaultTransactionDefinition一样的事务定义内容。

只通过@Transactional标注业务对象以及对象中的业务方法,并不会为业务方法带来任何事务管理的支持。@Transactional只是一个标志而已,需要我们在执行业务方法的时候,通过反射读取这些信息,并根据这些信息构建事务,才能使这些声明的事务行为生效。 这就好像下方代码清单中的代码所演示的那样。

public Quote getQuate() {try {Method method = quoteService.getClass().getDeclaredMethod("getQuate", null);boolean isTxAnnotationPresent = method.isAnnotationPresent(Transactional.class);if(!isTxAnnotationPresent) {return (Quote)quoteService.getQuate();}Transactional txInfo = method.getAnnotation(Transactional.class);TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);if(!txInfo.propagation().equals(Propagation.REQUIRED)) {transactionTemplate.setPropagationBehavior(txInfo.propagation().value());}if(txInfo.readOnly()) {transactionTemplate.setReadOnly(true);}// ...return (Quote)transactionTemplate.execute(new TransactionCallback() {public Object doInTransaction(TransactionStatus txStatus) {return quoteService.getQuate();}});} catch(SecurityException e) {e.printStackTrace(); // 不要这样做return null;} catch(NoSuchMethodException e) {e.printStackTrace(); // 不要这样做return nul1;}
}
...

不过,我们不用自己去写这些底层的逻辑了,通过在容器的配置文件中指定如下一行配置,这些搜寻注解、读取内容、构建事务等工作全都由Spring的IoC容器搞定:

<tx:annotation-driven transaction-manager="transactionManager"/>

所以,使用注解元数据驱动的声明式事务管理,基本上就需要做两件事:

  • 使用@Transactional标注相应的业务对象以及相关业务方法。这一步通常由业务对象的开发者统一负责,@Transactional的使用使得元数据以及业务逻辑的实现全部集中到了一处,有了IDE的支持,管理起来更是得心应手。

  • 在容器的配置文件中设定事务基础设施。我们需要添加<tx:annotation-driven transaction-manager=”transactionManager"/>以便有人使用我们所标注的@Transactional,并且,需要给出相应的事务管理器,要进行事务管理,没有它可不行。

对于QuoteService来说,在已经使用@Transactional标注了相应的事务管理信息之后,剩下的就是对应容器配置的内容了,详情见下方代码清单。

提示:Spring推荐将@Transactional标注于具体的业务实现类或者实现类的业务方法上。之所以如此,是因为SpringAOP可以采用两种方式来生成代理对象(动态代理或者CGLIB)。如果将@Transactional标注于业务接口的定义上,那么,当使用动态代理机制构建代理对象时,读取接口定义上的@Transactional信息是没有问题的。可是当使用CGLIB构建代理对象的时候,则无法读取接口上定义的@Transactional数据。

20.3 小结

本章主要阐述了如何使用Spring事务抽象框架进行事务管理的相关内容,分别就如何使用Spring进行编程式事务管理和声明式事务管理进行了详细的阐述。学习完本章,相信大家已经可以游刃于事务管理相关的日常开发工作了。Spring的事务抽象框架中使用到一些日常开发过程中常用的理念和方法。接下来,我们将尝试挖掘一下这些宝贵的经验,以为我用。

第20章 使用Spring进行事务管理(三)相关推荐

  1. 《Java EE企业级应用开发教程(SSM)》练习题答案---第五章Spring的事务管理(仅供参考)

    单选题 1.以下有关事务管理方式相关说法错误的是(). A.Spring中的事务管理分为两种方式:一种是传统的编程式事务管理,另一种是声明式事务管理 B.编程式事务管理:是通过AOP技术实现的事务管理 ...

  2. Spring的事务管理难点剖析:应用分层的迷惑

    2019独角兽企业重金招聘Python工程师标准>>> Web.Service及DAO三层划分就像西方国家的立法.行政.司法三权分立一样被奉为金 科玉律,甚至有的开发人员认为如果要使 ...

  3. Spring对事务管理的支持的发展历程(基础篇)

    1.问题 Java代码   Connection conn =    DataSourceUtils.getConnection();   //开启事务   conn.setAutoCommit(fa ...

  4. spring教程--事务管理

    Spring的事务管理 1.1 事务: 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性: ACID: 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致. 隔离性:一个事 ...

  5. Spring的事务管理难点剖析(4):多线程的困惑

    2019独角兽企业重金招聘Python工程师标准>>> 由于Spring的事务管理器是通过线程相关的ThreadLocal来保存数据访问基础设施(也即Connection实例),再结 ...

  6. Spring4.x❹ Spring的事务管理 DataSourceTransactionManager

    1 Spring事务管理? 2 Spring 事务管理案例 2.1 实体类 2.2 dao 2.3 service 2.4 Spring配置文件 2.5 test 3 service中的事务管理 3. ...

  7. 使用动态代理简单模拟一下spring的事务管理

    按照平时写代码的习惯,我们会定义一个service接口 package com.proxy.test; public interface UserService {public void sayHel ...

  8. Spring的事务管理1

    事务的回顾: 事务:逻辑上的一组操作,组成这组事务的各个单元,要么全部成功,要么全部失败 事务的特性:ACID 原子性(Atomicity):事务不可分割 一致性(Consistency):事务执行前 ...

  9. Spring JDBC-Spring事务管理之数据库事务基础知识

    概述 数据库事务的概念 原子性 一致性 隔离性 持久性 数据并发的问题 脏读dirty read 不可重复读unrepeatable read 幻象读 phantom read 幻象读和不可重复度的区 ...

最新文章

  1. webGL的一些咨询--web3D
  2. 逻辑模型设计步骤-关系模式定义
  3. 16. Logging 模块的配置与使用
  4. ios 容错处理JKDataHelper和AvoidCrash
  5. router3 BGP2 属性及选路
  6. 结构分析的计算机方法有哪些,第6篇 桥梁结构分析计算机方法.ppt
  7. EJB3与Spring的集成
  8. parseInt和parseFloat(转换成数字,提取成数字)?
  9. 如何正确选择合适的贷款机构,避免征信花掉?
  10. mysql 5.6 gtid 主从_MySQL5.6基于GTID的主从复制
  11. LeetCode 459. 重复的子字符串(Repeated Substring Pattern)
  12. 肥城市c语言入门自学零基础,2019年自考C语言程序设计模拟试题十三答案.doc
  13. 目录 1. 常见mime类型 1 1.1. 2.1.1. Type application 2 2.1.2. Type audio 22.1.3. Type image 32.1.4. Type t
  14. ISelectionMgr Interface
  15. js回避ie缓存的办法
  16. 网页中审查元素与查看网页源代码的区别
  17. Linux CRDA(Central Regulatory Domain Agent)简介
  18. 汽车车灯注塑件三维尺寸公差检测
  19. 【python】pdf转png
  20. 第十一章 文件操作_C语言实现文件复制功能(包括文本文件和二进制文件)

热门文章

  1. 一拍是多少秒 计算机制音乐,如何计算每分钟的节拍(BPM)的一首歌
  2. iPad 上 PDF-Expert 同步坚果云中的 pdf
  3. 多项式除法的C语言实现
  4. 莆田学院计算机系是不是师范类,莆田学院2010会计学、计算机科学与技术拟各招一个海外合作班...
  5. 如何在步进音序器中制作简单的节奏
  6. Day88.七牛云: 房源图片、用户头像上传 Common-upload、Webuploader
  7. 解析基因影响:孟德尔随机化的创新思维
  8. 炉石传说服务器维护有补偿吗,炉石传说2017年服务器故障回档补偿公告
  9. birt java api_BIRT使用API进行简单部署
  10. iframe在IE中的透明问题与allowTransparency属性