Spring 声明式事务

Spring的声明式事务管理是通过Spring AOP实现的,默认情况下,Spring事务只在遇见RuntimeException时才会回滚,可以通过配置来设置其他类型异常。

概念上来说,在事务代理上调用方法的工作过程看起来像这样:

基于@Transactional注解的事务方式

 

首先配置Spring容器:

 

<!-- 激活annotation功能 -->
<context:annotation-config />
<!-- 开启使用@Transactional注解方式 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<context:component-scan base-package="com.sohu.tv.crm"
    scoped-proxy="targetClass">
</context:component-scan>-->

<bean id="jdbcTemplate"  class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
      destroy-method="close">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
    <!-- 指定连接池里最小连接数 -->
    <property name="minPoolSize" value="2"/>
    <!-- 指定连接池里最大连接数 -->
    <property name="maxPoolSize" value="5"/>
    <!-- 连接最大空闲时间,超过时间将被丢弃,单位是秒 -->
    <property name="maxIdleTime" value="60"/>
    <!-- 当连接池里面的连接用完的时候,C3P0一次获取的新的连接数 -->
    <property name="acquireIncrement" value="3"/>
    <!-- 指定连接池里最大缓存多少个Statement对象 -->
    <property name="maxStatements" value="10"/>
    <!-- 初始创建连接的数量 -->
    <property name="initialPoolSize" value="2"/>
    <!-- 每隔XX秒检查连接池里的空闲连接 ,单位是秒 -->
    <property name="idleConnectionTestPeriod" value="900"/>
    <property name="numHelperThreads" value="10" />
    <property name="preferredTestQuery" value="select 1 from dual" />
</bean>
<!-- DataSourceTransactionManager是用于JDBC、ibatis/MyBatis类型的Spring内置事务管理器 -->
<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    <property name="rollbackOnCommitFailure" value="true" />
</bean>

 配置中的<tx:annotation-driven/>属性proxy-target-class决定为那些使用了@Transactional注解的类创建何种事务代理。 如果 "proxy-target-class" 属性被设为 "true", 那么基于类的代理就会被创建。即使用CGLib创建增强的代理,如果 "proxy-target-class"属性被设为"false" 或者没设,那么基于接口的标准JDK代理就会被创建。

 

@Transactional注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。 然而,请注意只是使用@Transactional注解并不会启用事务行为, 它仅仅 是一种元数据,能够被可以识别@Transactional注解和上述的配置适当的具有事务行为的beans所使用。其实是<tx:annotation-driven/>元素的出现 开启了事务行为。

Spring团队的建议是你只在具体的类上使用@Transactional注解, 而不要注解在接口上。我在实际项目中一般只在操作数据库的方法上使用,而不是在整个类上使用。

 

@Service
public class BlogServiceImpl implements BlogService {

    @Resource
    private BlogDao blogDao;

    @Override
    @Transactional
    public void insert(Blog blog) {
        // 一些其他逻辑...
        blogDao.insert(blog);
        // 一些其他逻辑...
    }

    @Override
    @Transactional
    public void update(Blog blog) {
        // 一些其他逻辑...
        blogDao.update(blog);
        // 一些其他逻辑...
    }

    @Override
    @Transactional
    public void insertOrUpdate(Blog blog) {
        if (blog.getId() == null) {
            insert(blog);
        } else {
            update(blog);
        }
    }

    @Override
    public Blog get(Integer id) {
        return blogDao.get(id);
    }
}

 这样在调用BlogService方法的时候,带有@Transactional的方法就会在事务的控制下。但是这里需要注意和Spring AOP一样的场景,即方法调用同一个类中的另一个方法时不能被拦截的问题。从上面来看,即insertOrUpdate方法中调用了insert()和update()方法,则insert()或者update()方法使用的实际是insertOrUpdate()方法的事务,如果insertOrUpdate()方法没有@Transactional注解,则调用该方法时,内部的insert()方法的事务是不起作用的,具体原因参见 http://my.oschina.net/mushui/blog/161387.

 

@Transactional注解属性

属性类型描述
propagation枚举型:Propagation可选的传播性设置
isolation枚举型:Isolation可选的隔离性级别(默认值:ISOLATION_DEFAULT)
readOnly布尔型读写型事务 vs. 只读型事务
timeoutint型(以秒为单位)事务超时
rollbackFor一组Class类的实例,必须是Throwable的子类一组异常类,遇到时 必须 进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。
rollbackForClassname一组Class类的名字,必须是Throwable的子类一组异常类名,遇到时 必须 进行回滚
noRollbackFor一组Class类的实例,必须是Throwable的子类一组异常类,遇到时 必须不 回滚。
noRollbackForClassname一组Class类的名字,必须是Throwable的子类一组异常类,遇到时 必须不 回滚

事务传播行为

Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,Spring共支持7种传播行为:

Required:默认的事务传播行为,表示必须有逻辑事务,否则新建一个事务,使用PROPAGATION_REQUIRED指定,表示如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务:

RequiresNew:创建新的逻辑事务,使用PROPAGATION_REQUIRES_NEW指定,表示每次都创建新的逻辑事务(物理事务也是不同的)因此外部事务可以不受内部事务回滚状态的影响独立提交或者回滚。

Supports:支持当前事务,使用PROPAGATION_SUPPORTS指定,指如果当前存在逻辑事务,就加入到该逻辑事务,如果当前没有逻辑事务,就以非事务方式执行。

NotSupported:不支持事务,如果当前存在事务则暂停该事务,使用PROPAGATION_NOT_SUPPORTED指定,即以非事务方式执行,如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行。

Mandatory:使用PROPAGATION_MANDATORY指定,如果当前有事务,使用当前事务执行,如果当前没有事务,则抛出异常(IllegalTransactionStateException)。

Never:不支持事务,如果当前存在是事务则抛出IllegalTransactionStateException异常,使用PROPAGATION_NEVER指定。

 

Nested:嵌套事务支持,使用PROPAGATION_NESTED指定,如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚。

Nested和RequiresNew的区别: 

  • RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;
  • Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而 RequiresNew由于都是全新的事务,所以之间是无关联的;
  • Nested使用JDBC 3的保存点实现,即如果使用低版本驱动将导致不支持嵌套事务。

    实际应用中一般使用默认的事务传播行为,偶尔会用到RequiresNew和Nested方式。

基于XML配置的事务

个人比较偏爱使用@Transactional注解的方式,但是很大一部分项目是使用XML方式的,使用XML方式只需要把上面配置中的

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

 去掉,然后添加如下配置即可:

<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <tx:method name="insert*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
       <tx:method name="get*" read-only="true"/>
   </tx:attributes>
</tx:advice>
<aop:config>
   <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.sohu.tv.crm.service.impl.*.*(..))" />
</aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值