Spring框架的使用指南(二)声明式事务篇

Spring框架的使用指南(二)声明式事务篇

前言:Spring除了提供IOC和AOP两大功能外,还提供了事务管理的功能。

所谓事务就是保证一个操作只有两种状态,完成状态或者初始状态,不会出现完成了99%的情况。我们为什么需要事务呢?用个最经典例子说明一下,甲在银行转账,账户有1000元,转给乙500元,如果转账成功,那么甲账户只有500元,乙账户增加500元。但是如果甲转账时,银行停电了呢,甲账户少了500元,但乙账户却因为停电中断了转账操作导致账户还是原来的数额。这样的情况明显是不合理,既然转账失败了,那钱就应该退回原账户才对。所以这时候我们就要用事务来保证转账操作要么成功,甲少了500元,乙增加500元,要么转账失败,甲还是原来的数额,乙也是原来的数额。

Spring事务

Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

事务的四个特性

1.原子性,事务由一系列动作组成,但这个操作无法被继续分割。原子性保证动作要么全部完成,要么一个都没有完成。

2.一致性,事务一旦完成,无论是成功还是失败,所有业务数据都是一致的,不能出现甲转了500,但乙却没有增加500的情况。

3.隔离性,可能有多个事务同事处理同一个数据,应保证事务之间隔离性,防止数据损坏。

4.持久性,一旦事务完成,不管发生什么系统错误,事务结果都不能再可变动,通常会将事务结果写入持久化存储器中。

事务的隔离级别

隔离级别定义了一个事务可能受其他并发事务影响的程度。 当多个事务并发处理相同的数据时就有可能出现下列问题。

脏读:后一个事务读取了前一个事务修改了却没有提交的值,如果此时发生异常,数据回滚了,此时后一个事务获得的数据就是无效的。

不可重复读:一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。

幻读:幻读与不可重复读类似。它发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据时。在随后的查询中,第一个事务就会发现多了一些原本不存在的记录。

隔离级别就是为了防止这些现象的出现,而定义的。

事务的传播行为

事务的传播行为是指,当一个事务方法被另一个事务方法调用时,两个事务是怎样的关系。例如方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

基于xml配置声明式事务管理

1.引入maven依赖

<!-- 使用jdbcTemplate操作数据库 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.1.7.RELEASE</version>
            </dependency>
             <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.16</version>
            </dependency>

2.配置事务管理器

<?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
        https://www.springframework.org/schema/context/spring-context.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">

    <!--配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatistest?characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=UTC"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    
		<!--配置数据源到jdbcTemplate中-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--开启自动代理-->

    <tx:advice transaction-manager="transactionManager" id="txAdvice">
        <tx:attributes>
            <!-- 支持当前事务,如果执行到get开头的任何方法时没有事务则开启一个事务 这是最常见的方式 -->
            <tx:method name="get*" rollback-for="java.lang.Exception" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
       <!-- 配置通知需要切入事务的类 -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* FrameWork.ServiceImpl.UserServiceImpl.*(..))"></aop:advisor>
    </aop:config>

</beans>

Spring并不直接管理事务,而提供一个Spring事务管理器的接口,通过这个接口,向各个框架提供了对应的事务管理器接口,将具体的事务管理的实现交给了各个框架。

org.springframework.transaction.PlatformTransactionManager

上面的事务管理器是基于jdbc的事务管理器

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

基于Hibernate的事务管理器

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

Java持久化API事务(JPA)

 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

Java原生API事务

如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:

 <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManagerName" value="java:/TransactionManager" />
    </bean>

JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。

tx:method标签代表哪些方法需要开启事务管理。

  1. rollback-for属性指定发生什么异常时回滚事务。

  2. propagation属性指定事务的传播行为,默认为REQUIRED
    (1) REQUIRED,,如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
    (2) SUPPORTS,如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
    (3) MANDATORY,如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
    (4) REQUIRES_NEW,重新创建一个新的事务,如果当前存在事务,暂停当前的事务。
    (5) NOT_SUPPORTED,以非事务的方式运行,如果当前存在事务,暂停当前的事务。
    (6) NEVER,以非事务的方式运行,如果当前存在事务,则抛出异常。
    (7) NESTED,和 Propagation.REQUIRED 效果一样。

  3. isolation属性指定事务的隔离级别,默认值为 Isolation.DEFAULT。

PS:若未指定rollback-for属性的值,或值为空,则只有发生RuntimeException异常时才会回滚事务。

基于注解配置声明式事务管理

package FrameWork;

import FrameWork.Advice.myAdvice;
import FrameWork.Service.MusicService;
import FrameWork.Service.UserService;
import FrameWork.ServiceImpl.MusicServiceImpl;
import FrameWork.ServiceImpl.UserServiceImpl;
import FrameWork.bean.Music;
import FrameWork.bean.User;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringConfig {

    @Bean
    public Music music()
    {
        return new Music();
    }

    @Bean
    public User user()
    {
        return new User();
    }

    @Bean
    public UserService userService()
    {
        return new UserServiceImpl();
    }

    @Bean
    public MusicService musicService()
    {
        return new MusicServiceImpl();
    }

    @Bean
    public myAdvice myAdvice()
    {
        return new myAdvice();
    }

    @Bean
    public DriverManagerDataSource driverManagerDataSource(){
        DriverManagerDataSource driverManagerDataSource=new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/mybatistest?characterEncoding=utf8&useSSL=true&serverTimezone=UTC");
        driverManagerDataSource.setPassword("root");
        driverManagerDataSource.setUsername("root");
        return driverManagerDataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
        JdbcTemplate jdbcTemplate=new JdbcTemplate();
        jdbcTemplate.setDataSource(driverManagerDataSource());
        return jdbcTemplate;
    }

    @Bean
    public DataSourceTransactionManager transactionManager(){
        DataSourceTransactionManager dataSourceTransactionManager=new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(driverManagerDataSource());
        return dataSourceTransactionManager;
    }

}

    @Transactional(rollbackFor = Exception.class)
    public void getUser(String name,String sign) throws Exception {
            jdbcTemplate.update("insert into user(name,sign)  values('"+name+"','"+sign+"')");
            throw new Exception("异常");
    }

从代码上可以看出,使用注解配置事务要比xml方便许多。

@EnableTransactionManagement注解代表开启事务支持,标注在配置类上。

@Transactional注解标记使用事务,如果注解在类上,则类中所有public方法使用事务,注解在方法上则只有该方法使用。

PS:@Transactional注解只能对public方法生效,注解到其他非public方法上时不会报错,但不使用事务。

这里是完全采用java配置的,你也可以选择使用xml配置bean和事务管理器,不配置tx:advice和aop:advisor标签,只需要在xml中加入下面的配置

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

此标签作用相当于@EnableTransactionManagement注解。你现在已经可以使用@Transactional注解配置事务了。

更多关于Spring事务的详细内容,请参考博客:https://www.cnblogs.com/yixianyixian/p/8372832.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值