spring中事务的传播行为

/ Spring / 3 条评论 / 663浏览

当我们在spring中调用Service中一个方法时,如果我们默认配置了对Service的事务管理,那么此时的Service将运行在一个由spring管理的事务环境中。由于在我们日常的开发时,通常会在一个Service接口中调用其它Service中的接口以此来完成一个完整的事务操作,这时就会发生服务接口嵌套调用的情况,spring通过事务传播行为控制当前事务如何传播到被嵌套调用的目标服务接口方法中的。下面我们想想了解一下在spring中都有哪些不同的事务传播行为,以前它们的区别。

spring在TransactionDefinition接口中定义了7种类型的事务传播行为,它们具体的区别如下:

事务传播行为说明
PROPAGATION_REQUIRED
如果当前没有事务,那就新建一个新的事务,如果已经存在一个事务,那就加入到这个事务中。
PROPAGATION_SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方法执行。
PROPAGATION_MANDATORY
使用当前事务,如果当前没有事务,则就抛出异常。
PROPAGATION_REQUIRES_NEW
新建事务,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

备注:当使有PROPAGATION_NESTED时,底层的数据源必须基于JDBC3.0,并且实现者需要支持保存点事务机制。

我们在实际开发时,基本不会通过编码的方式来进行事务管理,因为这样很不方便管理,在实际的开发中我们一般会采用xml的方法或者注解的方法来进行事务管理。虽然我们不会直接采用编码的方法进行事务管理,但在spring中还是提供了对该方法的支持。我们可以直接使用spring为我们提供的TransactionTemplate模板类来执行我们用编码的方式对事务进行管理。下面我们简单了解一下TransactionTemplate模板类的主要方法。

void setTransactionManager(PlatformTransactionManager transactionManager) 设置事务管理器
T execute(TransactionCallback<T> action) 在TransactionCallback回调接口中定义需要以事务方式处理数据访问逻辑

下面我们通过一个简单的测试用例来看一下到底怎么用ransactionTemplate模板类通过编码的方法进行事务管理。

public interface UserDao {
public void save(UserInfo userInfo);
}
public interface UserService {
public void save(UserInfo userInfo);
}
public class UserServiceImpl implements UserService {

@Autowired
private UserDao userDao;

@Autowired
private TransactionTemplate transactionTemplate;

@Override
public void save(UserInfo userInfo) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
userDao.save(userInfo);
}
});
}
}

由于spring事务管理器TransactionSynchronizationManager进行工作,所以如果在回调接口方法中需要显示的访问数据库连接,必须通过资源获取工具类得到线程绑定的数据连接,因为这是spring事务管理的底层协议。如果DAO接口是基于spring提供的模板类创建的,由于模板类已经在内部使用了资源获取工具类获取数据库连接,所以用户就不必关心底层数据连接的获取问题了。

上面我们提到过,在我们日常开发时,不是会采用用编码的方式管理事务的,我们基本上会采用声明式事务管理功能,因为这种方式可以让事务管理代码完全从业务代码中解耦。在spring中声明式事务管理是通过spring AOP的功能实现的。具体的流程是通过事务的声明信息,spring负责将事务管理增强逻辑动态织入到业务方法的相应连接点中。下面我们通过xml代码的方法来管理事务。

public interface UserDao {
public void save(UserInfo userInfo);
public UserInfo getUserInfo(int id);
}

UserDao接口中有两个方法,我们可以对对save方法添加写的事务能力,而对getUserInfo方法添加读事务能力。

public class UserServiceImpl implements UserService {

@Autowired
private UserDao userDao;

@Autowired
private TransactionTemplate transactionTemplate;

@Override
public void save(UserInfo userInfo) {
userDao.save(userInfo);
}

@Override
public UserInfo getUserInfo(int id) {
return userDao.getUserInfo(id);
}
}

我们可以看到,采用xml管理事务和直接用编码的方法有很大的不同的,在接口的实现类中只有相关的业务代码,并不会有任何相关事务的代码,而用直接编码的方法则必须手动的将管理事务的代码编写在接口的实现类中。在spring中可以用两种方式来通过xml管理事务,一种方式是TransactionProxyFactoryBean的方式来进行事务管理,还有一种方式是通过tx的方式来管理事务,因为TransactionProxyFactoryBean的方式spring官网中已经不推荐使用了,所以,我们下面的测试用例将采用用tx的方式来管理事务。

<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池-->
<property name="dataSource" ref="dataSource" />
</bean>
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* com.jilinwula..service.impl..*ServiceImpl.*(..))"/>
<aop:advisor pointcut-ref="serviceMethod" advice-ref="transactionAdvice"/>
</aop:config>

<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="do*"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>

tx:method标签的主要参数配置如下:

WX20171022-170502.png

  1. 评价太长好像报错啊

    回复
    1. @孙凯

      应该是超范围了

      回复
  2. 今天突然想学习一下事务,发现你这里有,你这评论也太复杂了

    回复