.为复杂的事务api提供了一致的编程模型,例如jta、hibernate、jpa等
.支持声明式事务管理
.非常好的整合spring的各种数据访问抽象
传统上,javaee开发者 有两个事务管理的选择:全局和本地。全局事务由应用服务器管理,使用api笨重的jta。局部事务是和资源相关的,比如一个和jdbc连接关联的事务,这个选择有深刻的含义,例如:全局事务可以有多可事务性的资源。使用局部事务,用用 服务器不需要参与事务管理,并且不能够保证跨越多个资源的事务的正确性
全局事务:全局事务有个很大的缺陷,代码需要用jta,一个笨重的api。使用全局事务限制了应用代码的重用性,因为jta只有在应用服务器的环境中才能使用。
本地事务:本地事务容易使用,但是也有明显的缺陷,他们不能用于多个事务性的资源,例如jdbc连接的事务管理的代码不能应用于全局的jta事务中,另一个缺点就是局部事务趋向于入侵式的编程模型。
spring解决了这些问题,它使得开发者能够在任何环境下使用一致的编程模型,spring同时提供声明式事务和编程式事务,声明式事务管理是多数开发者的首选,在多数情况下也是被推荐使用的。
使用编程式事务管理 ,开发者直接使用spring框架的事务抽象,这个抽象可以使用在任何底层事务基础之上。使用首选的声明式模型,开发者通常书写很少的或者没有事务相关的代码,因此不依赖spring框架或者任何其他事务api。
spring事务抽象的关键是事务策略的概念,由spring的PlatFormTransactionManager借口定义
1
2
3
4
5
6
|
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
|
PlatFormTransactionManager抛出的异常时unchecked Exception的,底层的事务失败几乎都是致命的,很少情况下应用程序代码可以从他们中恢复过来,不过应用开发者可以捕获并处理TransactionException。
TransactionDefinition接口;
事务隔离:当前事务和其他事务的隔离程度。
事务传播:通常在一个事务中执行的所有代码都会在这个事务中运行。但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性方法的执行行为:例如,简单地在现有的事务中继续运行(大多数情况);或者挂起现有事务,创建一个新的事务
事务超时:事务在超时后多久会背底层的事务设施回滚
只读状态:只读事务不修改任何的数据,在某种情况下是一种很有用的优化。
声明式事务管理:
spring的声明式事务管理使用spring aop实现的,由于spring致力于无侵入性,一般不需要理解aop的概念就可以进行声明式事务管理,
spring的声明式事务管理可以在任何环境下使用,只需要更新配置文件,就可以和jdbc、hibernate或其他的事务机制一起工作,并且这中管理机制可以应用到任何一个java类以及类的实例上,同样spring也提供了声明式的回滚规则。但是spring的声明式事务处理不提供高端应用服务器提供的跨越远程调用的事务上下文传播。
spring声明式事务管理最重要的概念是:spring的事务管理是通过aop实现的,其中的事务通知由目前基于XML或者注解的元数据驱动,代理对象与事务元数据结合产生了一个aop代理,它使用一个PlatformTransactionManager
的实现品配合TransactionInterceptor
在方法执行的前后过程中实施事务。
当spring框架的事务基础架构默认抛出运行时、unchecked Exception时,事务才会回滚,也就是说如果抛出一个RuntimeException或者其子类时候。从事务中抛出的checked exception不会进行回滚操作。我们可以在配置文件中给具体的方法加上rollback-for = NoProductInStockException。有时候就算你不想在异常抛出的时候回滚事务,就可以使用不会滚的规则,no-rollback-for = instrumentNotFoundException。当然除了声明式的事务回滚规则之外我们还可以通过编程式的方式来指定事务的回滚,例如下面
1
2
3
4
5
6
7
8
|
public void resolvePosition() {
try {
} catch (NoProductInStockException ex) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
|
虽然写法简单,但是不符合spring提倡的无入侵性,并且使你的代码与spring框架的事务高度耦合。
现在让我们考虑一下这样的场景,假设你有许多服务对象,你想为他们分别设置完全不同的事务语义。 在Spring中,你可以通过分别定义特定的<aop:advisor/>
元素, 让每个advisor采用不同的 'pointcut'
和'advice-ref'
属性,来达到目的。如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
< aop:config >
< aop:pointcut id = "defaultServiceOperation"
expression = "execution(* x.y.service.*Service.*(..))" />
< aop:pointcut id = "noTxServiceOperation"
expression = "execution(* x.y.service.ddl.DefaultDdlManager.*(..))" />
< aop:advisor pointcut-ref = "defaultServiceOperation" advice-ref = "defaultTxAdvice" />
< aop:advisor pointcut-ref = "noTxServiceOperation" advice-ref = "noTxAdvice" />
</ aop:config >
< bean id = "fooService" class = "x.y.service.DefaultFooService" />
< bean id = "anotherFooService" class = "x.y.service.ddl.DefaultDdlManager" />
< tx:advice id = "defaultTxAdvice" >
< tx:attributes >
< tx:method name = "get*" read-only = "true" />
< tx:method name = "*" />
</ tx:attributes >
</ tx:advice >
< tx:advice id = "noTxAdvice" >
< tx:attributes >
< tx:method name = "*" propagation = "NEVER" />
</ tx:attributes >
</ tx:advice >
|
除了基于xml配置文件的形式进行声明式事务配置外,我们还可以使用注解的方式进行声明式的事务管理,直接在java源码中声明事务语义的做法让事务和将受其影响的代码距离更近,而且一般来说不会有不恰当的耦合风险,@Transaction注解可用于接口定义和接口方法、类定义和类的public方法上,然而并不是加了这个注解就代开启了事务行为,它仅仅是一种元数据。我们用的时候尽量把注解加在类上,而不要加载接口上,因为注解是不能继承的,这就意味者如果你使用基于类的代理时,实物的设置将不能被基于类的代理所识别,而且对象也不会背事务代理包装。
编程式事务管理:
spring同样也提供两种编程式事管理
1.使用TransactionTemplate
2.直接使用一个PlatformTransactionManager实现
如果使用编程式事务管理的方式,应该尽量采用TransactionTemplate
TransactionTemplate采用与spring中别的模板(如jdbcTemplate)同样的方法,它使用回调机制,将应用代码从资源获取和释放代码中解放出来,这样写出的代码是目的驱动的,把精力集中在开发者想做的事情上面。但是使用TransactionTemplate绝对会增加你的代码与spring事务框架和api间的耦合,具体的事务管理方式还是要根据不同的情况选择。
如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class SimpleService implements Service {
in this instance
private final TransactionTemplate transactionTemplate;
PlatformTransactionManager
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null." );
this .transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
return transactionTemplate.execute( new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
|
如果你不需要返回值,也可以创建一个TransactionCallbackWithoutResult
的匿名类
1
2
3
4
5
6
|
transactionTemplate.execute( new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
|
回调方法中的代码可以通过TransactionStatus
的setRollbackOnly()
方法来回滚事务
1
2
3
4
5
6
7
8
9
10
|
transactionTemplate.execute( new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessExeption ex) {
status.setRollbackOnly();
}
}
});
|
诸如传播模式、隔离等级、超时等等的事务设置都可以在TransactionTemplate
中或者通过配置或者编程式地实现
1
2
3
4
5
6
7
8
9
|
public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null." );
this .transactionTemplate = new TransactionTemplate(transactionManager);
this .transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this .transactionTemplate.setTimeout( 30 ); // 30 seconds
}
}
|
TransactionTemplate
实例是线程安全的,任何状态都不会保存,TransactionTemplate
会维护配置的状态,所以当一些类共享一个单独的TransactionTemplate
实例时候,不需要考虑安全性的问题,但是如果一个类需要不同配置的TransactionTemplate
,比如不同的隔离登记,那就需要创建两个不同的的TransactionTemplate
。
第二种方式是PlatformTransactionManager
我们可以直接使用PlatformTransactionManager
管理我们的事务,只需要通过bean的应用,简单的把正在使用的PlatformTransactionManager
传递给bean,然后,使用TransactionDefinition
和TransactionStatus
对象,可以启动,回滚和提交事务,
1
2
3
4
5
6
7
8
9
10
11
12
|
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName( "SomeTxName" );
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
}
catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
|
那我们该选择编程式事务管理还是声明式事务管理呢?
当我们只有很少的事务操作时候看,编程式事务管理通常比较适合,例如有一个web应用,其中只有特定的更新操作有事务要求,你可能不愿意使用spring或者其他的技术设置事务代理,这种情况下,使用TransactionTemplate可能是个好办法,只有编程式事务管理才能显示的设置事务名称。
另一方面,如果你的应用中存在大量的事务操作,那么声明式事务管理通常是值得的,他将事务管理与业务逻辑分离,而且在spring中的配置也不难。