Spring——事务管理

事务管理简介

??Spring 事务管理有两种方式:一种是编程式事务管理,即通过编写代码实现事物管理,包括定义事务的开始,程序正常执行后的事物提交,异常时进行的事务回滚。另一种是基于AOP技术实现的声明式事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,我们只用关心核心业务逻辑代码,然后通过AOP技术将事务管理的“切面”代码织入到业务类中,声明式事务管理有包括基于AOP方式的事务管理和基于 @Transactional注解方式的事务管理,声明式事务管理极大的简化了编程式事务管理的操作流程,不再需要重复地执行定义事物的开始,程序正常执行后事务提交,异常时进行事物回滚这些繁琐的操作,而基于 @Transactional注解的声明式事务又进一步简化了基于AOP的事务管理,减少了Spring配置代码。

??声明式事务的缺点在于只能作用到方法级别,无法做到像编程式事务那样能控制到代码块级别。

  • 事务传播行为

??事务规则也就是事务传播行为,用于解决业务层方法之间的相互调用的问题题。常见的事物传播行为可分为以下几种:

名称 说明
REQUIRED 表示当前方法必须运行在一个事物环境中,如果一个现有的事物正在运行,该方法将运行在这个事务中,否则,就要开始一个新的事务
REQUIRESNEW 表示当前方法必须运行在自己的事务里
SUPPORTS 表示当前方法不需要事务处理环境,但如果有一个事务正在运行的话,则这个方法也可以运行在这个事务中
MANDATORY 表示当前方法必须运行在一个事务上下文中,否则就抛出异常
NEVER 表示当前方法不应该运行在一个事务上下文中,否则就抛出异常

??事务管理的主要任务是事务的创建,事务的回滚和事务的提交,是否需要创建事务及如何创建事务,是由事务传播行为控制的,通常数据的读取可以不需要事务管理,或者可以指定为只读事务,而对于数据的增加,删除和修改操作,则有必要进行事务管理。如果没有指定事务的传播行为,Spring默认采用REQUIRED。


转账案例

转账环境搭建

  • 创建表sql(三个账户,初始化金额都为1000)
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `money` double DEFAULT NULL,<br>
  PRIMARY KEY (`id`)<br>
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;<br>
INSERT INTO `account` VALUES (‘1‘, ‘aaa‘, ‘1000‘);<br>
INSERT INTO `account` VALUES (‘2‘, ‘bbb‘, ‘1000‘);<br>
INSERT INTO `account` VALUES (‘3‘, ‘ccc‘, ‘1000‘);<br>
  • 项目所需jar包

??因为我的jdk是1.8,如果spring的版本用spring3.X的话,后面会出现参数不匹配异常java.lang.IllegalArgumentException,所以我换成了Spring4。

  • 数据库连接参数

  • 日志文件log4j.properties
log4j.rootLogger=INFO,logfile,stdout

#write log into file
log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.Threshold=warn
log4j.appender.logfile.File=${webapp.root}\\logs\\Transaction.log
log4j.appender.logfile.DatePattern=.yyyy-MM-dd
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=[Transaction] %d{yyyy-MM-dd HH\:mm\:ss} %X{remoteAddr} %X{remotePort} %m %n

#display in console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Threshold=info
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[Transaction] %d{yyyy-MM-dd HH\:mm\:ss} %X{remoteAddr} %X{remotePort} %m %n

  • 创建业务层接口和实现类
package com.zzh.demo1;

public interface AccountService {
    /**
     *
     * @param out   :转出账号
     * @param in    :转入账号
     * @param money :转账金额
     */
    public void transfer(String out,String in,Double money);
}
package com.zzh.demo1;

public class AccountServiceImpl implements AccountService {
    //注入DAO类
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void transfer(String out, String in, Double money) {
        accountDao.outMoney(out, money);
        accountDao.inMoney(in, money);
    }

}
  • 创建DAO层接口和实现类
package com.zzh.demo1;

public interface AccountDao {
    /**
     * @param out   :转出账号
     * @param money :转出金额
     */
    public void outMoney(String out,Double money);
    /**
     * @param in    :转入账号
     * @param money :转入金额
     */
    public void inMoney(String in,Double money);
}

这里让Dao层实现类继承JdbcDaoSupport,这样一来就可以将JdbcTemplate注入其中,不过我是直接将DataSource连接池注入其中,这时我的DAO实现类就可以创建一个JdbcTemplate,然后操作数据库的相关内容。

package com.zzh.demo1;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
 * DAO层接口实现类
 * 这里使用的是JdbcDaoSupport模板
 *
 */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Override
    public void outMoney(String out, Double money) {
        String sql = "update account set money = money - ? where name = ?";
        this.getJdbcTemplate().update(sql,money,out);
    }

    @Override
    public void inMoney(String in, Double money) {
        String sql = "update account set money = money + ? where name = ?";
        this.getJdbcTemplate().update(sql,money,in);
    }

}

??当然也可以不选择让DAO层继承JdbcDaoSupport,而是把JdbcTemplate作为他的属性进行操作,通过setter方法利用JdbcTemplate的构造方法将dataSource传入。

package com.zzh.demo1;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class AccountDaoImpl implements AccountDao {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource){

        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public void outMoney(String out, Double money) {
        String sql = "update account set money = money - ? where name = ?";
        this.jdbcTemplate.update(sql,money,out);
    }

    @Override
    public void inMoney(String in, Double money) {
        String sql = "update account set money = money + ? where name = ?";
        this.jdbcTemplate.update(sql, money,in);
    }

}
  • 配置applicationContext.xml

要将c3p0的连接池注入DAO层实现类中,使其可以创建JdbcTemplate。

<?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:context="http://www.springframework.org/schema/context"
    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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 配置业务层 -->
    <bean id="accountService" class="com.zzh.demo1.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO层,注入连接池就可以得到jdbc模板-->
    <bean id="accountDao" class="com.zzh.demo1.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>
  • 测试转账效果

让测试运行于Spring测试环境中,并引入配置文件

注意Junit使用spring时,若spring没加载到log4j就会报以下警告:
log4j:WARN No appenders could be found for logger(org.springframework.test.context.junit4.SpringJUnit4ClassRunner).log4j:WARN Please initialize the log4j system properly.

测试业务层类,在用注解 @Resource(name="accountService")和private AccountService accountService;默认按名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。 @Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。

package com.zzh.demo1;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")

public class SpringTest {
    @Resource(name="accountService")
    private AccountService accountService;

    @Test
    public void demo1(){
        accountService.transfer("aaa", "bbb", 200d);
    }
}

我们让aaa向bbb转账200,查看结果转账成功。

此时如果我们故意在业务层实现类转账过程当中制造异常,如下所示:

查看结果:

可以看到aaa账户的钱减少了,而bbb账户的钱没有增加。因为现在还没有进行事务管理,所以出现了转出执行了,而转入没有执行这样的情况。而我们所希望的是转入与转出能够同时成功或者同时失败,这时我们就需要进行相应的事务管理了。


编程式事务管理

  • PlatformTransactionManager接口
    这个接口具体的实现类主要有用SpringJdbc或者Mybatis进行管理的类DataSourceTransactionManager,用Hibernate来进行事务管理的类HibernateTransactionManager
  • 配置SpringJdbc事务管理器

    将dataSource连接池注入管理器中,这时因为jdbc进行管理时需要获得连接对象,然后用connection.setAutoCommit(false)使其不自动提交,执行完后要conmmit,有异常的话要rollback。能获取这个连接的就是dataSource连接池,而DataSourceTransactionManager事务管理器是真正进行事务管理的类,而连接池又能获取到连接对象,所以要将其注入。

<!-- 配置SpringJdbc的事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
  • 配置事务管理模板

    如果直接用事务管理器来管理的话,代码会很繁琐,Spring为了简化事务管理的代码,提供了一个事务管理模板类org.springframework.transaction.support.TransactionTemplate。而真正进行事务管理的还是transactionManager,所以要将其注入事务管理模板中。

<!-- 配置事务管理模板 -->
    <bean id = "transactionTemplat" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>
  • 业务层实现类中注入事务管理模板

    在编程式事务管理中,需要我们在使用事务的时候,手动的编写代码。也就是哪个类需要进行事务管理,就将模板注入其中。

//注入事务管理模板
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

然后在配置文件中业务层类里注入模板Bean

    <bean id="accountService" class="com.zzh.demo1.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <!-- 注入事务管理模板 -->
        <property name="transactionTemplate" ref="transactionTemplate"/>
    </bean>

* 利用模板进行转账

??业务类的 transfer方法中的转入和转出此时存在于一个事务中,需要我们用模板的execute方法传入TransactionCallback接口,这里我选择new 一个匿名内部类TransactionCallbackWithoutResult。这个匿名内部类里的一个方法叫做doInTransactionWithoutResult,在里面进行事务的相关操作。

这里要有为注意我划红线的部分,在JDK 1.8以下的版本会报错,也就是必须在方法的局部变量前加上final关键字

??这是因为匿名内部类访问外部的局部变量时该变量必须是final,加上final之后会导致局部变量提升为一种隐式的成员变量,而在匿名类中需要传递进来final关键字,java希望的是保证内部实现和外在表现的一致性,正因如此,我内部类 操作事务时使用了外部类的out,in和money局部变量,所以必须要加。

??而我使用的是JDK 1.8却没有final也没报错是因为java8中已经没有匿名内部类和局部内部类只能访问final变量的限制了

  • 测试结果

    先将金额都改为1000,然后转账,事务回滚了。也就是达到了一个事务里的操作同时成功或者同时失败。


声明式事务管理

??在编程式的事务管理中,我们需要手动的去修改业务层的代码,这样对以后的开发是不太友好的。而声明式事务管理是基于AOP技术实现,其主要思想是将事务管理作为一个“切面”代码单独编写,我们只用关心核心业务逻辑代码,然后通过AOP技术将事务管理的“切面”代码织入到业务类中。

基于TransactionProxyFactoryBean的方式

  • 配置事务管理器
<?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:context="http://www.springframework.org/schema/context"
    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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 配置业务层 -->
    <bean id="accountService" class="com.zzh.demo2.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO层,注入连接池就可以得到jdbc模板-->
    <bean id="accountDao" class="com.zzh.demo2.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>
  • 配置业务层代理

??因为要在业务层上进行事务管理,我们可以产生一个代理类来对业务层的transfer方法进行增强。这个代理类叫做事务代理工厂类,它可以为业务层类产生一个代理对象。其中属性包括目标对象target即为我们需要去代理的类,然后是注入事务管理器进行事务管理,也就是对代理对象进行事务增强。而进行事务管理还需要一些具体的信息比如传播行为和隔离级别等等,所以还需配置transactionAttribute属性,然后再向这个属性中传入参数props,也就是键值对,key代表的是方法,这里只有一个方法transfer,也可以写一个 * 代表类中所有方法。而prop的值可以是PROPAGATION(事务的传播行为),ISOLATIONf(事务的隔离级别) -Exception(发生哪些异常回滚事务) +Exception(发生哪些事务不回滚)。在这里我只配了PROPAGATION_REQUIRED,这个传播行为代表业务层方法中的转入转出调用的时候,是处在一个事务之中。

 <!-- 配置业务层代理 -->
    <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 配置目标对象 -->
        <property name="target" ref="accountService"/>
        <!-- 注入事务管理器 -->
        <property name="transactionManager" ref="transactionManager"/>
        <!-- 注入事务的属性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="transfer">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
  • 业务层

    采取了AOP思想,业务层就不再需要配置多余的代码了。

package com.zzh.demo2;

public class AccountServiceImpl implements AccountService {
    //注入DAO类
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public void transfer( String out,  String in,  Double money) {
                accountDao.outMoney(out, money);
                int i = 1/0;
                accountDao.inMoney(in, money);
    }
}
  • 向测试类中注入代理类

    在编程式事务管理中, @ Resource是业务类,而现在业务类是没有被增强过的类,所以要替换为业务类增强后的代理类即 @Resource(name="accountServiceProxy")。

package com.zzh.demo2;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringDemo2 {

    /**
     * 注入代理类
     */
    @Resource(name="accountServiceProxy")
    private AccountService accountService;

    @Test
    public void demo(){
        accountService.transfer("aaa", "bbb", 200d);
    }
}
  • 测试

当没有异常时进行转账:

当设置异常之后,钱数没有发生改变,事务回滚了:

  • 添加readOnly之后

当设置为只读后,就不允许进行修改了


基于AspectJ的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:context="http://www.springframework.org/schema/context"
    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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 配置业务层 -->
    <bean id="accountService" class="com.zzh.demo3.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO层,注入连接池就可以得到jdbc模板-->
    <bean id="accountDao" class="com.zzh.demo3.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>
  • 配置事务的通知

??需要有事务的管理器,事务相关的属性,属性里面的method即为哪些方法要来执行你的事务,跟这个方法相关的还有相应的事务传播行为和隔离级别等等。

<!-- 配置事务的增强 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
  • 配置切面

??在切点当中的表达式第一个 * 代表方法的返回值,AccountService后的+代表他的子类,最后的 * (..)代表任意的方法和参数。切面则由切点和增强构成,即在这个切点上运用这个增强。代表切面的标签有两个,一个是 < aop:aspect > 代表多个切入点和多个通知,另一个是 < aop:advisor > 代表一个切入点和一个通知,这个例题里只有一个增强“txAdvice”,所以就使用了asvisor标签。

 <!-- 配置切面 -->
    <aop:config>
        <!-- 配置切点 -->
        <aop:pointcut expression="execution(* com.zzh.demo3.AccountService+.*(..))" id="pointcut1"/>
        <!-- 配置切面 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
    </aop:config>
  • aop自动代理

??我划线的部分不再需要修改,因为这种基于AspectJ的方式是属于自动代理,也就是在XML中没有显式配置 < aop:aspectj-autoproxy/ >不代表不使用自动代理,这条配置默认属性为“false”,表示只代理接口(JDK动态代理),所以如果只想代理接口,可以不用显式写出。也就是说现在的 < bean id="accountService" > 这个类在生成的时候就是一个代理对象了,所以 @Resource(name="accountService")已经是被增强过了。如果还对自动代理不清楚,请看我另一篇文章: Spring之AOP由浅入深

  • 测试

事务发生了回滚


基于注解配置声明式事务

  • 配置事务管理器
<?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:context="http://www.springframework.org/schema/context"
    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.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 配置业务层 -->
    <bean id="accountService" class="com.zzh.demo4.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置DAO层,注入连接池就可以得到jdbc模板-->
    <bean id="accountDao" class="com.zzh.demo4.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>
  • 开启注解事务
<tx:annotation-driven transaction-manager="transactionManager"/>

??哪个类需要使用事务管理,就在那个类头加上 @Transactional注解即可。

package com.zzh.demo4;

import org.springframework.transaction.annotation.Transactional;

@Transactional
public class AccountServiceImpl implements AccountService {
    //注入DAO类
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public void transfer( String out,  String in,  Double money) {
                accountDao.outMoney(out, money);
                int i = 1/0;
                accountDao.inMoney(in, money);
    }
}
  • @Transactional 注解中的属性

直接在注解后面即可添加相应的传播行为,隔离级别等等

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
  • 测试结果

事务发生了回滚

作者:六月的余晖 
出处:http://www.cnblogs.com/zhaozihan/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

原文地址:https://www.cnblogs.com/zyxiaohuihui/p/8455692.html

时间: 2024-10-10 01:43:03

Spring——事务管理的相关文章

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

Spring事务管理

写这篇博客之前我首先读了<spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

Spring事务管理接口PlatformTransactionManager的实现类DataSourceTransactionManager

package org.springframework.jdbc.datasource; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.springframework.beans.factory.InitializingBean; import org.springfra

Spring 事务管理高级应用难点剖析--转

第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN&en=utf&hpp=20&dws=cndw&lo=zh 概述 Spring 最成功,最吸引人的地方莫过于轻量级的声明式事务管理,仅此一点,它就宣告了重量级 EJB 容器的覆灭.Spring 声明式事务管理将开发者从繁复的事务管理代码中解脱出来,专注于业务逻辑的开发上,这是一件

Spring事务管理--多个ORM框架在使用时的情况分析

公司的项目已经接近尾声了,总结一下项目中用到的技术,我发现项目中的有些东西还是挺模糊的,只是知道这么用就行了.并不清楚其中的原理.由于公司的项目比较老,是7年前的一个项目了,中间一直有人在维护,也是在这个过程中不断融入了新的东西,比如就项目的持久化这块来说,就用了ibatis.mybatis.hibernate.spring JDBC四种混合的框架.究其原因只能说是历史遗留问题,就不做过多的解释了.但是这么多持久化的框架如何协同工作的,尤其是事务的控制,一个系统中使用如此多的持久化框架是,他们是

详细介绍Spring事务管理

在学习spring事务管理时,我忍不住要问,spring为什么进行事务管理,spring怎么进行的事务管理?首先,为什么要进行事务,接下来说说spring是怎样进行事务管理的. 我们都知道spring提供两种管理事务的方式,一种是声明式事务,一种是编程式事务. Spring的声明式事务管理,基于Spring的AOP,不再需要不停地写commit,rollback,(但Spring仍然没有放弃编程式的事务管理策略). Spring的编程式事务管理,为我们提供了一个TransactionTempla

Spring事务管理----------整合学习版

作者:学无先后 达者为先 Spring提供了一流的事务管理.在Spring中可以支持声明式事务和编程式事务. 一  spring简介 1 Spring的事务       事务管理在应用程序中起着至关重要的作用:它是一系列任务的组成工作单元,在这个工作单元中,所有的任务必须同时执行.它们只有二种可能执行结果,要么所有任务全部执行成功,要么所有任务全部执行失败.     Spring中提供了丰富的事务管理功能,它们超过了EJB并且和EJB一样支持声明式事务,重要的是Spring提供了一致的事务管理,

spring事务管理——编程式事务、声明式事务

本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带有 512MB 内存(

Spring事务管理-使用注解配置事务

一.概述 事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性.Spring Framework对事务管理提供了一致的抽象,其特点如下:为不同的事务API提供一致的编程模型,比如JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)支持声明式事务管理,特别是基于注解的声明式事务管理,简单易用 提供比其他事务API如JTA更简单的编程式事务管理

分布式事务系列(1.1)Spring事务管理器PlatformTransactionManager

1 系列目录 分布式事务系列(开篇)提出疑问和研究过程 分布式事务系列(1.1)Spring事务管理器PlatformTransactionManager源码分析 2 jdbc事务 2.1 例子 public void save(User user) throws SQLException{ Connection conn=jdbcDao.getConnection(); conn.setAutoCommit(false); try { PreparedStatement ps=conn.pre