Spring事务心得

 最近对事务的了解与应用用的比较多,所以现在来总结下自己的心得,传统上,J2EE开发者有两个事务管理的选择: 全局 或 本地 事务。全局事务由应用服务器管理,使用JTA。局部事务是和资源相关的,比如一个和JDBC连接关联的事务。这个选择有深刻的含义。例如,全局事务可以用于多个事务性的资源(典型例子是关系数据库和消息队列)。使用局部事务,应用服务器不需要参与事务管理,并且不能帮助确保跨越多个资源(需要指出的是多数应用使用单一事务性的资源)的事务的正确性。

全局事务. 全局事务有一个重大的缺陷,代码需要使用JTA:一个笨重的API(部分是因为它的异常模型)。此外,JTA的UserTransaction通常需要从JNDI获得,这意味着我们为了JTA,需要 同时 使用JNDI 和 JTA。显然全部使用全局事务限制了应用代码的重用性,因为JTA通常只在应用服务器的环境中才能使用。 以前,使用全局事务的首选方式是通过EJB的 CMT(容器管理事务):CMT是 声明式事务管理 的一种形式(区别于 编程式事务管理)。EJB的CMT不需要任何和事务相关的JNDI查找,虽然使用EJB本身肯定需要使用JNDI。它消除了大多数(不是全部)硬编码的方式去控制事务。重大的缺陷是CMT绑定在JTA和应用服务器环境上,并且只有我们选择使用EJB实现业务逻辑,或者至少处于一个事务化EJB的外观(Facade)后才能使用它。EJB有如此多的诟病,尤其是存在其它声明式事务管理时,EJB不是一个吸引人的建议。

本地事务. 本地事务容易使用,但也有明显的缺点:它们不能用于多个事务性资源。例如,使用JDBC连接事务管理的代码不能用于全局的JTA事务中。另一个缺点是局部事务趋向于入侵式的编程模型。

由于项目采用的是struts2.0+spring2.0+hibernate3.0框架,项目开发过程中对多表关联操作比较多.考虑到代码的重用与效率问题,决定把对每个表的操作方法封装到一个独立的DAO类里,再把事务控制抽象到业务逻辑层里来控制.
由于Spring框架引人注目的重要因素之一是它全面的事务支持。Spring框架提供了一致的事务管理抽象,这带来了以下好处:
1.为复杂的事务API提供了一致的编程模型,如JTA、JDBC、Hibernate、JPA和JDO
2.支持 声明式事务管理
3.提供比大多数复杂的事务API(诸如JTA)更简单的,更易于使用的 编程式 事务管理API
4.非常好地整合Spring的各种数据访问抽象

项目初始阶段由于对spring的声明式事务了解的不是很深入,由于项目的开发时间很紧,对技术的采用不加思索的采用了自己最熟悉的技术,如是用到spring的编程式事务来控制程序的事务。代码如下:
PlatformTransactionManager transManager = getTransactionTemplate().getTransactionManager();
// 定义TransactionDefinition:
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
transDef.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 开始一个Transaction:
TransactionStatus ts = transManager.getTransaction(transDef);

try {

//调用DAO层的方法
// 提交事务:
transManager.commit(ts);

}
catch(RuntimeException e) {
e.printStackTrace();
// 回滚事务:
transManager.rollback(ts);
}

假如service层有多个方法需要用的事务处理,就得必须重复copy此段代码。这样写代码也比较郁闷,出现代码的重复,冗余比较严重。
如是还是打算花点时间来研究spring的声明式事务的用法与原理。俗话说得好“长痛不如短痛!”。

大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合 非侵入式 轻量级容器的理念。
Spring的声明式事务管理是通过Spring AOP实现的,因为事务方面的代码与Spring绑定并以一种样板式风格使用,不过尽管如此,你一般并不需要理解AOP概念就可以有效地使用Spirng的声明式事务管理。
从考虑EJB CMT和Spring声明式事务管理的相似以及不同之处出发是很有益的。它们的基本方法是相似的:都可以指定事务管理到单独的方法;如果需要可以在事务上下文调用 setRollbackOnly() 方法。不同之处在于:
1.不像EJB CMT绑定在JTA上,Spring声明式事务管理可以在任何环境下使用。只需更改配置文件,它就可以和JDBC、JDO、Hibernate或其他的事务机制一起工作。
2.Spring的声明式事务管理可以被应用到任何类(以及那个类的实例)上,不仅仅是像EJB那样的特殊类。
3.Spring提供了声明式的回滚规则:EJB没有对应的特性,我们将在下面讨论。回滚可以声明式的控制,不仅仅是编程式的。
4.Spring允许你通过AOP定制事务行为。例如,如果需要,你可以在事务回滚中插入定制的行为。你也可以增加任意的通知,就象事务通知一样。使用EJB CMT,除了使用setRollbackOnly(),你没有办法能够影响容器的事务管理。
5.Spring不提供高端应用服务器提供的跨越远程调用的事务上下文传播。如果你需要这些特性,我们推荐你使用EJB。然而,不要轻易使用这些特性。通常我们并不希望事务跨越远程调用。

回滚规则的概念比较重要:它使我们能够指定什么样的异常(和throwable)将导致自动回滚。我们在配置文件中声明式地指定,无须在Java代码中。同时,我们仍旧可以通过调用 TransactionStatus 的 setRollbackOnly() 方法编程式地回滚当前事务。通常,我们定义一条规则,声明 MyApplicationException 必须总是导致事务回滚。这种方式带来了显著的好处,它使你的业务对象不必依赖于事务设施。典型的例子是你不必在代码中导入Spring API,事务等。

对EJB来说,默认的行为是EJB容器在遇到 系统异常(通常指运行时异常)时自动回滚当前事务。EJB CMT遇到 应用异常(例如,除了 java.rmi.RemoteException 外别的checked exception)时并不会自动回滚。默认式Spring处理声明式事务管理的规则遵守EJB习惯(只在遇到unchecked exceptions时自动回滚),但通常定制这条规则会更有用。
自己在项目用到的声明式事务的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<!--定义业务逻辑服务类-->

<!-- 定义查询模板字段服务类 -->
<bean id="queryFieldService" class="com.zdvictory.worktable.query.service.impl.QueryFieldServiceImpl">
<property name="queryFieldDAO" ref="queryFieldDAO" />
</bean>

<!-- 定义用户查询模板服务类 -->
<bean id="queryTemplateService" class="com.zdvictory.worktable.query.service.impl.QueryTemplateServiceImpl">
<property name="queryTemplateDAO" ref="queryTemplateDAO" />
<property name="queryViewResultDAO" ref="queryViewResultDAO" />
<property name="queryUserCondDAO" ref="queryUserCondDAO"/>
<property name="queryFieldDAO" ref="queryFieldDAO"/>
<property name="queryStatisDAO" ref="queryStatisDAO"/>
</bean>

<!-- 定义查询结果服务类 -->
<bean id="queryService" class="com.zdvictory.worktable.query.service.impl.QueryServiceImpl">
<property name="queryTableRelationDAO" ref="queryTableRelationDAO"/>
<property name="queryServiceDAO" ref="queryServiceDAO" />
</bean>


<!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="*" />
<tx:method name="find*,view*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->

<aop:config>
<aop:pointcut id="queryOperation" expression="execution(* com.zdvictory.worktable.query.service.IQuery*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="queryOperation"/>
</aop:config>


<!--定义DAO-->

<!-- 模板基础数据字段表的DAO -->
<bean id="queryFieldDAO" class="com.zdvictory.worktable.query.dao.impl.QueryFieldDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"/>
</bean>


<!-- 用户查询模板表的DAO -->
<bean id="queryTemplateDAO" class="com.zdvictory.worktable.query.dao.impl.QueryTemplateDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>

<!-- 查询_结果展示DAO -->
<bean id="queryViewResultDAO" class="com.zdvictory.worktable.query.dao.impl.QueryViewResultDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>


<!-- 查询_用户定制的查询条件表DAO -->
<bean id="queryUserCondDAO" class="com.zdvictory.worktable.query.dao.impl.QueryUserCondDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>


<!-- 查询_业务表关系的DAO -->
<bean id="queryTableRelationDAO" class="com.zdvictory.worktable.query.dao.impl.QueryTableRelationDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"/>
</bean>

<!-- 查询结果显示DAO -->
<bean id="queryServiceDAO" class="com.zdvictory.worktable.query.dao.impl.QueryServiceDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- 查询统计DAO -->
<bean id="queryStatisDAO" class="com.zdvictory.worktable.query.dao.impl.QueryStatisDAOImpl">
<property name="hibernateTemplate" ref="hibernateTemplate" />
</bean>
</beans>


来看下面这段代码:
<!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="*" />
<tx:method name="find*,view*" read-only="true"/>
</tx:attributes>
</tx:advice>

主要引用了一个事务管理对象“transactionManager”,然后定义方法的读写权限问题。

看看下面 这段代码:
<aop:config>
<aop:pointcut id="queryOperation" expression="execution(* com.zdvictory.worktable.query.service.IQuery*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="queryOperation"/>
</aop:config>
主要是通过Spring AOP代理实现对com.zdvictory.worktable.query.service包下,以IQuery*开头的类的所有方法的事务控制。
这种声明式事务控制的事务边界在方法调用前开始事务,在方法返回时提交事务。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值