事务分为全局事务和本地事务。
全局事务:是把不同的资源组织在一个事务里管理,(例如数据库操作和消息队列组织在一个事务里)通常使用JTA实现。JTA具有复杂的API,并且JTA中UserTransaction通常使用JNDI获取。这就意味着你需要在JNDI环境下使用全局事务,很明显这种方式降低了代码的可用性,因为JTA通常在应用服务器环境下可用。
本地事务:是针对特定资源操作的事务,也就是说,在事务的整个过程,事务只影响一种资源,事务成功,这种资源状态发生变化,事务失败,资源状态恢复到事务执行前(如数据库操作JDBC事务)。但它有一个严重的不足:它不能做在多事务资源环境下,例如:在jdbc事务下运行的代码,不能在在JTA事务环境下运行。因为应用服务器。因为应用服务器没有与事务管理器关联,它不能保证多个资源在一个事务里(大多数情况,都是单一资源的事务)。另外一个缺陷是使用本地事务是一种侵入式编程,对编程模式有影响。
Spring事务模式:解决了全局事务和本地事务的不足,它采用一致的编程模式使用本地和全局事务。你只需写代码一次,就可以在不同的事务管理策略环境下运行。Spring提供声明式和编程式两种方式使用事务。大多数人喜欢用声明式事务。
使用编程式事务,开发人员只需要学习Spring事务操作方法,使用spring事务编写的代码可以运行在不同的事务环境下。使用声明式事务,开发人员在开发时,不需要关心事务操作的内容。你编写的代码也不依赖于任何Spring事务的API。
Spring事务的介绍
Spring事务的关键是事务策略的概念,spring中事务策略的定义在接口:org.springframework.transaction.PlatformTransactionManager。
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
TransactionException 是继承与java.lang.RuntimeException,在AOP模式下,事务只有捕获java.lang.RuntimeException类的异常才会回滚。
getTransaction(TransactionDefinition)方法,根据参数TransactionDefinition返回TransactionStatus,一个TransactionStatus代表一个新的事务或者是程序上下文中已经存在的事务。已经存在的事务上下文是与当前执行的线程相关联的。
TransctionDefinition 接口定义下列内容:
Isolation: 事务的隔离级别;
Propagation:事务的传播性:通常一个标注事务业务方法方法,运行在一个事务里面,但是,你可以设置propagration改变这种行为。通过设置此值,可以使程序关联上下文中已存在的事物,也可以使用从新启动一个新的事务等行为。
TimeOut:事务开始后,过了设置的时间还没结束,事务会滚。
Read-only status:只读事务用于读取数据而没有修改数据的场景,在某些情况下使用,会带来一定的好处,比如,使用Hibernate。
TransactionStatus接口提供了事务执行控制和查询事务状态的信息。它是提供事务操作的基础APIs:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
使用Spring时,不论你是否选择声明式事务,还是编程式事务,定义正确的PlatformTransactrionManger的实现类对象都是必须的。通常使用依赖注入的方式定义此对象。
PlatformTransactionManager的实现类对象定义,要结合特定情况,如是使用JDBC,JTA,Hibernate还是其它。下面的例子是定义一个本地的PlatformTransactionManager实现类的定义。
定义JDBC DataSource
< 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 >
定义PlatformTransactionManager实现类对象,定义时,通过属性名为“dataSource"的引用与上面的数据源关联。
< bean id = "txManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = "dataSource" ref = "dataSource" />
< /bean >
如果使用java EE容器里的JTA和DataSource,使用JNDI使JtaTransactionManager和数据源关联在一起。如下面的配置:
< ?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 = "
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd" >
< jee:jndi-lookup id = "dataSource" jndi-name = "jdbc/jpetstore" />
< bean id = "txManager" class = "org.springframework.transaction.jta.JtaTransactionManager /〉
< !-- other definitions here -- >
< /beans >
JtaTransactionManager不需要指定dataSource,因为它使用容器的全局事务管理器。
HibernateTransactionManager 的配置
<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使用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 = "
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd" >
< jee:jndi-lookup id = "dataSource" jndi-name = "jdbc/jpetstore" />
< bean id = "txManager" class = "org.springframework.transaction.jta.JtaTransactionManager /〉
<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>
< !-- other definitions here -- >
< /beans >
使用事务同步资源
Spring有高水平和低水平两种方式使用事务同步资源。比较好的方式是使用高水平方式。
高水平方式是使用基于持久层APIs或ORM原始APIs的模板,如(JDBCTemplate),这些模板与事务关联,并且内部处理资源的创建、重用、销毁。开发人员使用这些模板编写代码时,不需要关心事务相关的事情,只需关注自己的业务逻辑。
低水平方式,开发人员需要自己使用事务原始APIs管理事务。DataSourceUtils(for jdbc)、EntityManagerFactoryUtils (for JPA), SessionFactoryUtils (for Hibernate), PersistenceManagerFactoryUtils (forJDO)等类是低水平方式下提供事务操作的APIs。例如:在使用传统的jdbc时,使用DataSource的getConnection()获取数据库连接。而使用 DataSourceUtils时,使用下列方式获取连接
Connection conn = DataSourceUtils.getConnection(dataSource);
如果程序上下文中存在事务,则返回与此事务关联的连接实例,否则重新创建一个新的连接实例。SQLExeption被CannotGetJdbcConnectionException 包裹,CannotGetJdbcConnectionException是一个RuntimeException。它返回的信息比SQLExeption丰富。更容易定位错误。
这种方法可以不依赖Spring事务管理器运行,在没有事务的环境中也能独立运行。
TransactionAwareDataSourceProxy
更低水平的事务应用类,它是一个DataSource的代理类,它能觉察spring事务管理容器中的事务管理器。它与java ee容器管理的DataSource有些类识。
使用声明式事务
声明式事务具有以下特定:
1、可以运行在任何环境下,如jta,jdbc、hibernate、jpa、jdo本地事物。这些功能通过配置就可以完成。
2、声明式事务对业务类的编写不做什么要求,可以用于任何类。
3、Spring提供rollback rules的配置,声明式和编程式事务都可以使用rollback rules
rollback rules 是一个重要的概念,它是用于指定发生什么样的异常,才会引起数据库回滚。你在配置文件里配置它,而不是使用代码设置。注意,能够引起事务回滚的异常必须是RuntimeException类或其子类。
声明式事务是通过AOP动态代理的方式实现,事务的配置数据通过注解或配置文件配置,调用业务方法时,通过代理调用
TransactionInterceptor 拦截器启动事务功能。
使用配置文件使用声明式事务
接口定义:
// the service interface that we want to make transactional
package x.y.service;
public interface FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName,ourier"> String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
实现类
// an implementation of the above interface
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();
}
}
配置文件
< !-- from the file 'context.xml' -- >
<?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-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
< !-- this is the service object that we want to make transactional -- >
<bean id="fooService" class="x.y.service.DefaultFooService"/>
< !-- the transactional advice (what 'happens'; see the bean below) -- >
<tx:advice id="txAdvice" transaction-manager="txManager">
< !-- the transactional semantics... -- >
<tx:attributes>
< !-- all methods starting with 'get' are read-only -- >
<tx:method name="get*" read-only="true"/>
< !-- other methods use the default transaction settings (see below) -- >
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
< !-- ensure that the above transactional advice runs for any execution
.1 Reference Documentation 319
of an operation defined by the FooService interface -- >
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
< !-- don't forget the DataSource -- >
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
< !-- similarly, don't forget the PlatformTransactionManager -- >
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
< !-- other definitions here -- >
</beans>
通过注解使用声明式事务
定义接口
// the service class that we want to make transactional Foo getFoo(String fooName); Foo getFoo(String fooName, String barName); void insertFoo(Foo foo); void updateFoo(Foo foo); |
配置信息
< !-- from the file 'context.xml' -- > xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" < !-- this is the service object that we want to make transactional -- > < !-- enable the configuration of transactional behavior based on annotations -- > < !-- a PlatformTransactionManager is still required -- > < !-- other definitions here -- > </beans> |
Property | Type | Description |
value | String | Optionalqualifierspecifyingthe |
transaction managertobeused. | ||
propagation | enum: Propagation | Optionalpropagationsetting. |
isolation | enum: Isolation | Optionalisolationlevel. |
readOnly | boolean | Read/write vs. read-only |
transaction | ||
timeout | int(insecondsgranularity) | Transactiontimeout. |
rollbackFor | Array of Class objects, which | Optional array of exception |
must be derived from | classesthat must causerollback. | |
Throwable. | ||
rollbackForClassname | Array of class names. Classes | Optional array of names of |
must be derived from | exception classes that must | |
Throwable. | causerollback. | |
noRollbackFor | Array of Class objects, which | Optional array of exception |
must be derived from | classes that must not cause | |
Throwable. | rollback. | |
noRollbackForClassname | Array of String class names, | Optional array of names of |
which must be derived from | exception classesthat must not | |
Throwable. | causerollback. |
使用注解方式实现多事务编程
接口定义
< jee:jndi-lookup id = "dataSource" jndi-name = "jdbc/jpetstore" /> < bean id = "txManager" class = "org.springframework.transaction.jta.JtaTransactionManager" /> |