Spring之事务管理

1.Spring事务管理的两种方式

Java EE应用的事务策略分为全局事务和局部事务。大多数情况下,我们都使用局部事务,所以这篇文章就不说全局事务了。

而Spring 框架为局部事务提供了两种管理方式,分别如下

(1)编程式事务管理:通过编程实现,但我们太懒了,所以一般不用。

(2)声明式事务管理:通过声明实现。

而声明式事务管理又可以通过以下两种方式实现

  • 基于配置文件实现
  • 基于注解的实现

2.Spring事务管理的API

Spring框架提供的事务管理抽象层主要由org.springframework.transaction包下的三个接口组成:

  • TransactionDefinition接口:用于描述事务的隔离级别、传播规则、超时时间、是否为只读事务等特性,可以编程设置这些特性,也可以通过XML文件或者注解配置。

  • TransactionStatus接口:用于描述事务的状态,事务管理器通过该接口可以获取事务的运行期状态信息,也可以通过该接口间接的回滚事务,它相比于在抛出异常时回滚事务的方式更具有可控性。

  • PlatformTransactionManager接口:该接口代表事务管理器,是Spring事务管理的核心接口。

针对不同的持久化技术,Spring提供了PlatformTransactionManager接口的不同实现类:

  • JpaTransactionManager:使用JPA持久化的事务管理器。

  • HibernateTransactionManager:使用Hibernate持久化的事务管理器。

  • DataSourceTransactionManager:使用Spring JDBC、iBatis等DataSource数据源持久化时的事务管理器。

  • JdoTransactionManager:使用JDO持久化的事务管理器。

  • JtaTransactionManager:使用全局事务的事务管理器。

3.步骤

无论是使用XML配置事务还是使用注解配置事务,都有以下几步
(1)配置数据源
(2)配置事务管理器
(3)配置事务增强
(4)配置事务增强的切面

准备工作

导jar包,这里我们使用了c3p0连接池,所以导入了c3p0的jar包

9.png

引入约束

<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" 
        xmlns:jdbc="http://www.springframework.org/schema/jdbc"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/jdbc 
        http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd"> 
</beans>

新建数据库account

11.png

新建dao和service

public class AccountDao {
    // JdbcTemplate实例
    private JdbcTemplate jdbcTemplate;
    
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    /**
     * 账户减少钱
     */
    public void lessAccount() {
        String sql = "update account set money = money - ? where name = ?";
        jdbcTemplate.update(sql, 1000, "CodeTiger");
    }
    
    /**
     * 账户增加钱
     */
    public void moreAccount() {
        String sql = "update account set money = money + ? where name = ?";
        jdbcTemplate.update(sql, 1000, "xu");
    }
}
public class AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    public void accountMoney() {
        // 一个账户减少钱
        accountDao.lessAccount();
        
        // 提现事务的功能
        int a = 10 / 0;
        System.out.println(a);
        
        // 一个账户增加钱
        accountDao.moreAccount();
    }
}

基于XML配置文件的事务管理

(1)配置数据源

    <!-- 配置c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="1311664842"></property>
    </bean>

配置了c3p0连接池一些最基本的参数,仅用于连接。

(2)配置事务管理器

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

不同的持久化技术,使用的事务管理器不一样,这里我们使用JDBC的事务管理器。

(3)配置事务增强

使用<tx:advice>标签,可以为一个或一批(通过通配符的方式进行方法名称的匹配)方法配置事务增强。

<tx:advice>标签有两个属性

  • id:唯一标识。

  • transaction-manager:已经配置的事务管理器bean的id,如果事务管理器bean的id值为transactionManager,则可以省略此属性。

<tx:advice>标签具有<tx:attributes>子标签,通过<tx:attributes>标签的子标签<tx:method>可以配置需要被事务增强的方法,以及事务的传播、隔离、超时、只读事务、对指定异常回滚和对指定异常不会滚的属性。<tx:method>标签的属性如下:

  • name:配置需要被事务增强的方法名,可以使用通配符*。

  • propagation:配置事务的传播类型。可选择REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED之一,默认为REQUIRED。

  • isolation:配置事务的隔离级别。可选择DEFAULT、READ_UNCOMMITED、READ_COMMITED、REPEATABLE_READ、SERIALIZABLE之一,默认为DEFAULT。

  • timeout:配置事务超时时间(以秒为单位),默认为-1,表示事务超时的时间由底层的事务系统决定。

  • read-only:配置是否为只读事务,默认为false。

  • rollback-for:配置需要自动回滚事务的异常类型,默认所有RuntimeException都会回滚。

  • no-rollback-for:配置不触发自动回滚事务的异常类型,默认所有CheckedException都不会回滚。

下面的代码配置事务增强

    <!-- 配置事务增强 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 做事务操作 -->
        <tx:attributes>
            <!-- 设置进行事务操作的方法匹配原则 -->
            <tx:method name="account*"/>
        </tx:attributes>
    </tx:advice>

(4)配置事务增强的切面

这一步将事务增强与切入点关联在一起。配置ide方法与普通的切面配置相同。

    <!-- 配置切面 -->
    <aop:config>
        <!-- 配置切入点,注意这里的切入点是service,不是dao -->
        <aop:pointcut expression="execution(* com.codeliu.service.AccountService.*(..))" id="pointcut1"/>
        <!-- 把增强用到切入点上 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
    </aop:config>

事务管理也使用了AOP的原理,即把事务增强看成切面,service看成切点(当然也可以把dao看成切点,这时事务增强的方法匹配规则就得相应的进行修改)

当然,配置文件中还少了一些东西,比如service和dao的实例化,以及获取操作数据库的实例对象。把它们也加上。

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.codeliu"></context:component-scan>
    
    <!-- 开启aop操作 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <!-- 配置Jdbc Template -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <bean id="accountDao" class="com.codeliu.dao.AccountDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
    <bean id="accountService" class="com.codeliu.service.AccountService">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

最后写一个测试方法进行测试

    @Test
    public void accountTest() {
        @SuppressWarnings("resource")
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService service = (AccountService)context.getBean("accountService");
        service.accountMoney();
    }

运行测试,出现除0异常,刷新数据库,账户余额没有发生变化,说明事务管理生效了。

基于注解的事务管理

(1)启用注解式事务配置

使用注解配置事务,首先需要在XML配置文档中开启事务的注解

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

该标签有三个属性

  • transaction-manager:已经配置好的事务管理器Bean的id,如果未指定,则会查找id为transactionManager的事务管理器Bean。
  • proxy-target-class:是否通过cglib创建子类代理对象。
  • order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。

(2)使用@Transactional注解

业务类或业务类的方法如果需要被增强,则需要使用@Transactional注解进行配置。

@Transactional注解的参数和前面讲的<tx:method>标签的属性非常类似。通常情况都使用默认值,不需要改动。

@Transactional注解可用于接口、接口的方法、类、类的public方法上,由于注解的不可继承性,通常建议在类上使用该注解。

如下,给service类加注解

// 开启事务,对该类的所有方法都生效
@Transactional
public class AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    @Transactional(readOnly = true)
    public void accountMoney() {
        // 一个账户减少钱
        accountDao.lessAccount();
        
        // 体现事务的功能
        int a = 10 / 0;
        System.out.println(a);
        
        // 一个账户增加钱
        accountDao.moreAccount();
    }
}

上面的代码中,给AccountService 类加了@Transactional注解,给accountMoney方法也加了@Transactional注解并设置为只读。因此最终的配置结果就是AccountService 类的所有方法都会被增强,并且accountMoney方法使用的是只读事务。(覆盖了类上的注解)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • 1.数据库事务基础知识 1.1.何为数据库事务 数据库事务的4个特性 原子性:组成一个事务的多个数据库操作是一个不...
    小螺钉12138阅读 1,571评论 1 18
  • 这部分的参考文档涉及数据访问和数据访问层和业务或服务层之间的交互。 Spring的综合事务管理支持覆盖很多细节,然...
    竹天亮阅读 1,038评论 0 0
  • Spring事务_事务的使用_05 编程式事务 在一般使用过程中一般不需要手动编程式实现事务管理,不过Spring...
    老猫头阅读 1,011评论 0 0
  • Spring 的 AOP 支持 Spring 的 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其...
    WesleyLien阅读 1,372评论 0 1
  • 第二节课作业:如何用系统思维打造个人ip 定义 听课的时候我就一直在想个人IP到底是个什么样的东西?是和ip网址一...
    004d4d8f2697阅读 153评论 0 1