Spring提供事务管理API:
PlatformTransactionManager 平台事务管理器.
commit(TransactionStatus status)
rollback(TransactionStatus status)
getTransaction(TransactionDefinition definition)
Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现
org.springframework.jdbc.datasource.DataSourceTransactionManager
使用Spring JDBC或iBatis 进行持久化数据时使用org.springframework.orm.hibernate3.HibernateTransactionManager
使用Hibernate3.0版本进行持久化数据时使用org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化时使用
org.springframework.jdo.JdoTransactionManager 当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager
使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
TransactionDefinition:事务定义
ISOLation_XXX:事务隔离级别.
ISOLATION_DEFAULT:默认级别.
Mysql repeatable_read oracle read_commited
ISOLATION_READ_UNCOMMITTED
ISOLATION_READ_COMMITTED
ISOLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE
PROPAGATION_XXX:事务的传播行为.(不是JDBC中有的,为了解决实际开发问题.)
事务的传播行为:(不是JDBC事务管理,用来解决实际开发的问题.)传播行为
解决业务层之间的调用的事务的关系.
1. PROPAGATION_REQUIRED :支持当前事务,如果不存在 就新建一个,A,B 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)
PROPAGATION_SUPPORTS :支持当前事务,如果不存在,就不使用事务,A,B 如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.
PROPAGATION_MANDATORY :支持当前事务,如果不存在,抛出异常,A,B 如果A有事务,B使用A的事务,如果A没有事务,抛出异常.PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务,A,B 如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务,A,B 非事务的方式运行,A有事务,就会挂起当前的事务.
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行,基于SavePoint技术,A,B A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚.
常用:(重点)
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_NESTED
TransactionStatus:事务状态
是否有保存点
是否一个新的事务
事务是否已经提交
关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态:状态由TransactionStatus记录.
Spring管理事物
手动编码完成事物管理(使用TransactionTemplate)
1.IOC配置dao
如果dao层使用了JdbcDaoSupport,那么可以直接注入dataSource或者jdbcTemplate
2.IOC配置service
dao(service需要调用dao层)
transactionTemplate(service需要使用transactionTemplate来管理实务)
3.IOC配置TransactionManager,根据不同的技术使用不用的类的实现
dataSource
4.IOC配置TransactionTemplate
transactionManager
transactionTemplate.execute(new TransactionCallBackWithoutResult)
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
/**
* 模拟账户1向账户2汇款
* @param money
*/
public void out(double money) {
String sql = "update account set money=money-100 where id =1 ";
this.getJdbcTemplate().update(sql);
int a = 1/0;
}
/**
* 模拟账户2接收账户1的汇款
* @param money
*/
public void in(double money) {
String sql = "update account set money=money+100 where id =2 ";
this.getJdbcTemplate().update(sql);
}
}
public class AccountService {
private AccountDao accountDao;
private TransactionTemplate transactionTemplate;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void transfer(final double money){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
accountDao.out(money);
accountDao.in(money);
}
});
}
}
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="yzgylq"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--
在这里我们使用的是spring jdbc的方式,我们的dao的实现了会继承JDBCDaoSupport这个类,这里我们注入了dataSource,JDBCDaoSupport中的setDataSource方法内会为我们创建jdbcTemplate对象
-->
<bean id="accountDao" class="com.njust.learning.spring.transaction.AccountDaoImpl">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--TransactionTemplate用于简化我们的事物管理,本质上就相当于设置一些事物的属性,startTransaction,rollback等操作更方便-->
<bean id="accountService" class="com.njust.learning.spring.transaction.AccountService">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!--事务管理的配置-->
<!--因为这里使用的是jdbc的方式进行数据层的操作,所以需要使用DataSourceTransactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--使用自己编码的方式进行事务的处理,这里使用的是事务的处理模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
这种方式就相当于最原始的自己手动创建Enhancer的方式进行动态代理
缺点
需要在自己的代码里面添加transactionTemplate.execute,对自己的代码造成了入侵
解决:可以使用springaop的动态代理,生成源service的代理对象,在代理对象中进行事务的管理
声明式事物管理原始的方式
使用TransactionProxyFactoryBean对需要事物管理的方法的动态代理
applicationContext.xml
1. dataSource
2. dao
dataSource或者jdbcTemplate
3. service
dao
4. transactionManager
5. TransactionProxyFactoryBean
注意事项:
target即目标对象必须要有,可能eclipse不能提示这个属性
prop的key就是方法名,表示这个方法需要进行事物管理
prop内容的格式:
PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
顺序:传播行为、隔离级别、事务是否只读、发生哪些异常可以回滚事务(所有的
异常都回滚)、发生了哪些异常不回滚.
例如:
+java.lang.ArithmeticException,就表示发生了这个异常还是可以提交事物
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(final double money){
accountDao.out(money);
accountDao.in(money);
}
}
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="yzgylq"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean id="accountDao" class="com.njust.learning.spring.transaction.AccountDaoImpl">
<property name="dataSource" ref="datasource"></property>
</bean>
<bean id="accountService" class="com.njust.learning.spring.transaction.AccountService">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--基于动态代理的事务管理-->
<bean id="serviceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"></property>
<property name="target" ref="accountService"></property>
<!--这个就相当于通知Advice,可以配置不同的方法的事物属性,如果不配置,则使用默认的事物属性-->
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
这种方式相当于使用ProxyFactoryBean对已经存在的对象进行动态代理增强
原始的方式实现事物的管理的缺点:对于每一个service都需要手动配置TransactionProxyFactoryBean很麻烦
使用Aspect实现service的动态代理
tx:advice(用于配置增强,主要用于配置对于不同的方法的事物属性进行配置)
aop:config(用于配置切面,切面就是切点和增强的结合,由于是一个切点和一个切面,所以
使用advisor)
即上面的dao的代码,一模一样
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/test"></property>
<property name="username" value="root"></property>
<property name="password" value="yzgylq"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean id="accountDao" class="com.njust.learning.spring.transaction.AccountDaoImpl">
<property name="dataSource" ref="datasource"></property>
</bean>
<bean id="accountService" class="com.njust.learning.spring.transaction.AccountService">
<property name="accountDao" ref="accountDao"></property>
</bean>
<tx:advice id="transaction_advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" isolation="REPEATABLE_READ"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="mypointcut"
expression="execution(* com.njust.learning.spring.transaction.AccountService.*(..))"/>
<aop:advisor advice-ref="transaction_advice" pointcut-ref="mypointcut"></aop:advisor>
</aop:config>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
这种方式不需要创建代理对象,是基于后处理bean的形式的,在对象的创建的过程就会进行方法的增强