1.声明式事务管理
Spring2.0及以后的版本中声明式事务的配置与之前的版本有相当大的不同。主要差异在于不再需要配置TransactionProxyFactoryBean了。
1.1基于XML Schema
//我们想做成事务性的服务接口
package x.y.service;
public interface FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
//上述接口的一个实现
package x.y.service;
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
throw new UnsupportedOperationException();
}
public Foo getFoo(String fooName, String barName) {
throw new UnsupportedOperationException();
}
public void insertFoo(Foo foo) {
throw new UnsupportedOperationException();
}
public void updateFoo(Foo foo) {
throw new UnsupportedOperationException();
}
}
<? 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="
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/tx[/url] [url]http://www.springframework.org/schema/tx/spring-tx-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url] [url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
<!-- =================================== -->
<!-- 加载属性文件 -->
<!-- =================================== -->
< bean id ="jdbcConfiguration"
class ="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
< property name ="locations" >
< list >
< value >classpath:jdbc-config.properties </ value >
</ list >
</ property >
</ bean >
<!-- =================================== -->
<!-- 配置数据源 -->
<!-- =================================== -->
< bean id ="dataSource" class ="org.apache.commons.dbcp.BasicDataSource" destroy-method ="close" >
< property name ="driverClassName" value ="${jdbc.driverClassName}" />
< property name ="url" value ="${jdbc.url}" />
< property name ="username" value ="jdbc.username" />
< property name ="password" value ="${jdbc.password}" />
</ bean >
< bean id ="txManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name ="dataSource" ref ="dataSource" />
</ bean >
<!-- =================================== -->
<!-- 配置事务 -->
<!-- =================================== -->
< aop:config >
< aop:pointcut id ="defaultServiceOperation" expression ="execution(* x.y.service.*Service.*(..))" />
< aop:advisor pointcut-ref ="defaultServiceOperation" advice-ref ="defaultTxAdvice" />
< aop:pointcut id ="noTxServiceOperation" expression ="execution(* x.y.service.ddl.DefaultDdlManager.*(..))" />
< aop:advisor pointcut-ref ="noTxServiceOperation" advice-ref ="noTxAdvice" />
</ aop:config >
< tx:advice id ="defaultTxAdvice" transaction-manager ="txManager" >
< tx:attributes >
< tx:method name ="get*" read-only ="true" />
< tx:method name ="*" />
</ tx:attributes >
</ tx:advice >
< tx:advice id ="noTxAdvice" transaction-manager ="txManager" >
< tx:attributes >
< tx:method name ="*" propagation ="NEVER" />
</ tx:attributes >
</ tx:advice >
<!-- =================================== -->
<!-- 配置业务Bean -->
<!-- =================================== -->
<!--
this bean will be transactional (c.f. the 'defaultServiceOperation' pointcut)
-->
< bean id ="fooService" class ="x.y.service.DefaultFooService" />
<!-- this bean will also be transactional,
but with totally different transactional settings -->
< bean id ="anotherFooService" class ="x.y.service.ddl.DefaultDdlManager" />
</ beans >
更进一步:参照Spring2.0参考手册--9.5.5. <tx:advice/> 有关的设置
1.2使用 @Transactional
//the service class that we want to make transactional
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
<? 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:tx ="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/tx[/url] [url]http://www.springframework.org/schema/tx/spring-tx-2.0.xsd[/url]" >
<!-- =================================== -->
<!-- 加载属性文件 -->
<!-- =================================== -->
< bean id ="jdbcConfiguration"
class ="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
< property name ="locations" >
< list >
< value >classpath:jdbc-config.properties </ value >
</ list >
</ property >
</ bean >
<!-- =================================== -->
<!-- 配置数据源 -->
<!-- =================================== -->
< bean id ="dataSource" class ="org.apache.commons.dbcp.BasicDataSource" destroy-method ="close" >
< property name ="driverClassName" value ="${jdbc.driverClassName}" />
< property name ="url" value ="${jdbc.url}" />
< property name ="username" value ="jdbc.username" />
< property name ="password" value ="${jdbc.password}" />
</ bean >
< bean id ="txManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name ="dataSource" ref ="dataSource" />
</ bean >
<!-- =================================== -->
<!-- 配置事务 -->
<!-- =================================== -->
<!-- enable the configuration of transactional behavior based on annotations -->
< tx:annotation-driven transaction-manager ="txManager" />
<!-- =================================== -->
<!-- 配置业务Bean -->
<!-- =================================== -->
<!-- this is the service object that we want to make transactional -->
< bean id ="fooService" class ="x.y.service.DefaultFooService" />
</ beans >
更进一步:参照Spring2.0参考手册--9.5.6.1. @Transactional 有关的设置
1.3插入事务操作
1.3.1使用 @Transactional
<!-- the service class that we want to make transactional -->
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
package x.y;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;
public class SimpleProfiler implements Ordered {
private int order;
// allows us to control the ordering of advice
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
// this method is the around advice
public Object profile(ProceedingJoinPoint call) throws Throwable {
Object returnValue;
StopWatch clock = new StopWatch(getClass().getName());
try {
clock.start(call.toShortString());
returnValue = call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
return returnValue;
}
}
<? 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="
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/tx[/url] [url]http://www.springframework.org/schema/tx/spring-tx-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url] [url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
< bean id ="fooService" class ="x.y.service.DefaultFooService" />
<!-- this is the aspect -->
< bean id ="profiler" class ="x.y.SimpleProfiler" >
<!-- execute before the transactional advice (hence the lower order number) -->
< property name ="order" value ="1" />
</ bean >
< tx:annotation-driven transaction-manager ="txManager" />
< aop:config >
<!-- this advice will execute around the transactional advice -->
< aop:aspect id ="profilingAspect" ref ="profiler" >
< aop:pointcut id ="serviceMethodWithReturnValue" expression ="execution(!void x.y..*Service.*(..))" />
< aop:around method ="profile" pointcut-ref ="serviceMethodWithReturnValue" />
</ aop:aspect >
</ aop:config >
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->
</ beans >
1.3.2基于XML Schema
<? 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="
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/tx[/url] [url]http://www.springframework.org/schema/tx/spring-tx-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url] [url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
<!-- DefaultFooService的代码跟前面一样,只是没有@Transactional注释-->
< bean id ="fooService" class ="x.y.service.DefaultFooService" />
<!-- the profiling advice -->
< bean id ="profiler" class ="x.y.SimpleProfiler" >
<!-- execute before the transactional advice (hence the lower order number) -->
< property name ="order" value ="1" />
</ bean >
< aop:config >
< aop:pointcut id ="entryPointMethod" expression ="execution(* x.y..*Service.*(..))" />
<!-- will execute after the profiling advice (c.f. the order attribute) -->
< aop:advisor advice-ref ="txAdvice" pointcut-ref ="entryPointMethod" order ="2" />
<!-- order value is higher than the profiling aspect -->
< aop:aspect id ="profilingAspect" ref ="profiler" >
< aop:pointcut id ="serviceMethodWithReturnValue" expression ="execution(!void x.y..*Service.*(..))" />
< aop:around method ="profile" pointcut-ref ="serviceMethodWithReturnValue" />
</ aop:aspect >
</ aop:config >
< tx:advice id ="txAdvice" transaction-manager ="txManager" >
< tx:attributes >
< tx:method name ="get*" read-only ="true" />
< tx:method name ="*" />
</ tx:attributes >
</ tx:advice >
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->
</ beans >
1.4Spring1.x版本中声明式事务的配置:
1.4.1配置TransactionProxyFactoryBean
<? xml version ="1.0" encoding ="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
< beans >
< bean id ="baseTxProxy" lazy-init ="true" class ="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract ="true" >
< property name ="transactionManager" > < ref bean ="transactionManager" /> </ property >
< property name ="target" > < ref bean ="daoTarget" /> </ property >
< property name ="transactionAttributes" >
< props >
< prop key ="*" >PROPAGATION_REQUIRED </ prop >
</ props >
</ property >
</ bean >
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->
</ beans >
1.4.2利用bean继承简化事务代理的配置
<? xml version ="1.0" encoding ="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
< beans >
< bean id ="baseTxProxy" lazy-init ="true" class ="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract ="true" >
< property name ="transactionManager" > < ref bean ="transactionManager" /> </ property >
< property name ="transactionAttributes" >
< props >
< prop key ="do*" >PROPAGATION_REQUIRED </ prop >
< prop key ="get*" >PROPAGATION_REQUIRED </ prop >
< prop key ="find*" >PROPAGATION_REQUIRED </ prop >
< prop key ="load*" >PROPAGATION_REQUIRED </ prop >
< prop key ="update*" >PROPAGATION_REQUIRED </ prop >
< prop key ="store*" >PROPAGATION_REQUIRED </ prop >
< prop key ="save*" >PROPAGATION_REQUIRED </ prop >
< prop key ="delete*" >PROPAGATION_REQUIRED </ prop >
< prop key ="print*" >PROPAGATION_REQUIRED </ prop >
</ props >
</ property >
</ bean >
< bean id ="testTrans1" parent ="baseTxProxy" >
< property name ="target" >
< bean class ="x.y.Test1" />
</ property >
</ bean >
< bean id ="testTrans2" parent ="baseTxProxy" >
< property name ="target" >
< bean class ="x.y.Test2" />
</ property >
</ bean >
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->
</ beans >
1.4.3根据BeanName自动创建事务代理
<? xml version ="1.0" encoding ="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
< beans >
<!-- 定义BeanNameAutoProxyCreator-->
< bean class ="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" >
<!-- 指定对满足哪些bean name的bean自动生成业务代理 -->
< property name ="beanNames" >
<!-- 下面是所有需要自动创建事务代理的bean-->
< list >
< value >test1 </ value >
< value >test2 </ value >
</ list >
<!-- 此处可增加其他需要自动创建事务代理的bean-->
</ property >
<!-- 下面定义BeanNameAutoProxyCreator所需的事务拦截器-->
< property name ="interceptorNames" >
< list >
< value >transactionInterceptor </ value >
<!-- 此处可增加其他新的Interceptor -->
</ list >
</ property >
</ bean >
< bean id ="transactionInterceptor" class ="org.springframework.transaction.interceptor.TransactionInterceptor" >
<!-- 事务拦截器bean需要依赖注入一个事务管理器 -->
< property name ="transactionManager" ref ="transactionManager" />
< property name ="transactionAttributes" >
<!-- 下面定义事务传播属性-->
< props >
< prop key ="insert*" >PROPAGATION_REQUIRED </ prop >
< prop key ="find*" >PROPAGATION_REQUIRED,readOnly </ prop >
< prop key ="*" >PROPAGATION_REQUIRED </ prop >
</ props >
</ property >
</ bean >
<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->
</ beans >
2.编程式事务管理
不建议使用,略。
3.PlatformTransactionManager
使用Spring时,无论你选择编程式还是声明式的事务管理,定义一个正确的 PlatformTransactionManager 实现都是至关重要的。
一般来说,选择PlatformTransactionManager实现时需要知道当前的工作环境,如JDBC、JTA、Hibernate等。
JDBC:
< bean id ="dataSource" class ="org.apache.commons.dbcp.BasicDataSource" destroy-method ="close" >
< property name ="driverClassName" value ="${jdbc.driverClassName}" />
< property name ="url" value ="${jdbc.url}" />
< property name ="username" value ="${jdbc.username}" />
< property name ="password" value ="${jdbc.password}" />
</ bean >
< bean id ="txManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name ="dataSource" ref ="dataSource" />
</ bean >
JTA:
<? 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:jee ="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/jee[/url] [url]http://www.springframework.org/schema/jee/spring-jee-2.0.xsd[/url]" >
< jee:jndi-lookup id ="dataSource" jndi-name ="jdbc/jpetstore" />
< bean id ="txManager" class ="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- other <bean/> definitions here -->
</ beans >
Hibernate局部事务:
< bean id ="dataSource" class ="org.apache.commons.dbcp.BasicDataSource" destroy-method ="close" >
< property name ="driverClassName" value ="${jdbc.driverClassName}" />
< property name ="url" value ="${jdbc.url}" />
< property name ="username" value ="${jdbc.username}" />
< property name ="password" value ="${jdbc.password}" />
</ bean >
< bean id ="sessionFactory" class ="org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
< property name ="dataSource" ref ="dataSource" />
< property name ="mappingResources" >
< list >
< value >org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml </ value >
</ list >
</ property >
< property name ="hibernateProperties" >
< value >
hibernate.dialect=${hibernate.dialect}
</ value >
</ property >
</ bean >
< bean id ="txManager" class ="org.springframework.orm.hibernate3.HibernateTransactionManager" >
< property name ="sessionFactory" ref ="sessionFactory" />
</ bean >
Hibernate全局事务:
<? 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:jee ="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url] [url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/jee[/url] [url]http://www.springframework.org/schema/jee/spring-jee-2.0.xsd[/url]" >
< jee:jndi-lookup id ="dataSource" jndi-name ="jdbc/jpetstore" />
< bean id ="sessionFactory" class ="org.springframework.orm.hibernate3.LocalSessionFactoryBean" >
< property name ="dataSource" ref ="dataSource" />
< property name ="mappingResources" >
< list >
< value >org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml </ value >
</ list >
</ property >
< property name ="hibernateProperties" >
< value >
hibernate.dialect=${hibernate.dialect}
</ value >
</ property >
</ bean >
< bean id ="txManager" class ="org.springframework.transaction.jta.JtaTransactionManager" />
<!-- other <bean/> definitions here -->
</ beans >