Spring事务

目录

 1、什么是事务

1.1事务的四大特征(ACID)

1.2事务并发出现的问题:

2、Spring事务

2.1事务三大接口

3、编程式事务管理和声明式事务管理

3.1编程式事务管理

3.2声明式事务管理

4.事务的传播性

5.事务失效的场景

1、事务方法访问修饰符非public,导致事务失效

2、方法内部调用     

3、异常类型错误

4、抛出的异常被吃了

5、数据表本身是不支持事务,导致事务失效

6.多线程调用

7.错误的传播特性

8.未被spring容器管理


 1、什么是事务

        要么一起成功,要么一起失败。

1.1事务的四大特征(ACID)

        原子性、一致性、隔离性、持久性

1.2事务并发出现的问题:

        脏读:A事务读取了 B事务未提交的数据,然后B回滚了

        不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据操作并提交,导致事务A读取到的数据不一致

        幻读:一个事务读取到了别的事务插入的数据 导致前后不一致 一般就是多了一行(系统管理员A将学生成绩从具体分数划分为ABCDE等级,但是这个时候系统管理B又插入了一条具体分数的记录,当A操作结束后发现还有一条记录没有改过来,就好像发生了幻觉 )

2、Spring事务

2.1事务三大接口

         PlatformtransactionManager:事务管理器

        TransactionDefinition:事务的基础信息,如设置超时时间、隔离基本、传播属性

        TransactionStatus:事务的状态信息,如是否是个新事务。是否被标记为回滚。

3、编程式事务管理和声明式事务管理

3.1编程式事务管理

创建一个maven项目首先在pom.xml中导入包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springtran</groupId>
    <artifactId>spring_tran</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.23</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
         <!--xml配置 声明式 事务使用 transfer3()-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.5.4</version>
        </dependency>
    </dependencies>
</project>

resources下面创建applicationContext.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"
       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">
    <!--
        注解 扫描包
    -->
    <context:component-scan base-package="com.javastatus.demo"/>
    <!--
        1、配置数据源
    -->
    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="username" value="tk"/>
        <property name="password" value="tk"/>
        <property name="url" value="jdbc:mysql://172.16.196.56:3306/test?serverTimezone=Asia/Shanghai"/>

    </bean>
    <!--
        2、提供事务管理器(编程式事务对应 方式一)
    -->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--
    3、配置TransactionTemplate(编程式事务对应 方式二)
    -->
    <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>
    <!--
    4、配置JdbcTemplate
    -->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

创建UserService测试

@Component
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    PlatformTransactionManager transactionManager;
    @Autowired
    TransactionTemplate transactionTemplate;

    public void transfer2() {//编程式事务 使用TransactionTemplate 方式二 
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    jdbcTemplate.update("update user set money=? where username =? ", 9, "张三");
                } catch (DataAccessException e) {
                    e.printStackTrace();
                    status.setRollbackOnly();
                }
            }
        });

    }
    public void transfer() {//编程式事务 使用TransactionManager 方式一
        //定义默认的事务
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        //获取TransactionStatus
        TransactionStatus transaction = transactionManager.getTransaction(definition);
        try {
            jdbcTemplate.update("update user set money=? where username =? ", 9, "张三");
            transactionManager.commit(transaction);
        } catch (DataAccessException e) {
            e.printStackTrace();
            transactionManager.rollback(transaction);
        }
    }
}

创建测试类UserDemo

public class UserDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx =new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService bean = ctx.getBean(UserService.class);
        bean.transfer3();
    }
}

完成测试 

3.2声明式事务管理

        实现方式可以xml配置、java配置、xml+java混合配置。

xml配置

事务管理器上面配置过了,直接第二步,第三步。在applictionContext.xml追加

   <!--
        xml配置三个步骤
        1.配置事务管理器
        2.配置事务通知
        3.配置AOP
    -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*"/>
            <tx:method name="insert*"/>
            <tx:method name="update*"/>
            <tx:method name="transf*"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="pc1" expression="execution(* com.javastatus.demo.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
    </aop:config>

测试方法 

    public void transfer3() {
        jdbcTemplate.update("update user set age=? where username =? ", 91, "3");

    }

java配置

创建一个配置类JavaConfig

@Configuration
@ComponentScan(basePackages = "com.javastatus.demo")
@EnableTransactionManagement//开始事务的注解支持
public class JavaConfig {
    @Bean
    DataSource dataSource(){
        DriverManagerDataSource ds =new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUsername("adm");
        ds.setPassword("adm");
        ds.setUrl("jdbc:mysql://172.16.196.56:3306/test?serverTimezone=Asia/Shanghai");
        return ds;
    }
    @Bean
    JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }
    @Bean
    PlatformTransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }
}

 创建一个UserService2

@Component
public class UserService2 {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    PlatformTransactionManager transactionManager;
    @Transactional
    public void transfer4() {
        jdbcTemplate.update("update user set age=? where username =? ", 99, "3");

    }


}

创建测试类UserDemo2

public class UserDemo2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        UserService2 bean = context.getBean(UserService2.class);
        bean.transfer4();
    }
}

测试的时候需要将UserService中的@componet注释掉

//@Component
public class UserService {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    PlatformTransactionManager transactionManager;
    @Autowired
    TransactionTemplate transactionTemplate;
    public void transfer3() {
        jdbcTemplate.update("update user set age=? where username =? ", 91, "3");

    }
    public void transfer2() {//编程式事务 方式二
    }
    。。。。。
}

xml+java混合配置

在applictionContext.xml追加,然后在方法上添加@transactional

     <!--
        javv+xml配置两个步骤
        1.配置事务管理器
          配置事务通知(不需要)
          配置AOP(不需要)
        2.<tx:annotation-driven/> 开启事务注解支持 在方法上添加@Transactional 就直接添加事务
        这一个标签 替代了配置事务通知、配置AOP这两步
        <tx:annotation-driven/>  等于 <tx:advice></tx:advice>+<aop:config></aop:config>
    -->
    <tx:annotation-driven/>

4.事务的传播性

约束条件说明
REQUIRED如果当前没有事务,则新建事务,如果当前存在事务,则加入当前事务,合并成一个事务
REQUIRES_NEW新建事务,如果当前存在事务,则把当前事务挂起,新建事务执行完后再恢复当前事务
NESTED如果当前没有事务,则新建事务,如果当前存在事务,则创建一个当前事务的子事务(嵌套事务),子事务不能单独提交,只能和父事务一起提交
SUPPORTS支持当前事务,如果当前没有事务,以非事务的方式执行
NOT_SUPPORTED以非事务方式执行,如果存在当前事务就把当前事务挂起
NEVER以非事务方式执行,如果当前存在事务就抛异常
MANDATORY使用当前事务,如果当前没有事务,就抛异常

5.事务失效的场景

1、事务方法访问修饰符非public,导致事务失效

          有final或者static关键字,导致事务失效

2、方法内部调用     

  • test1 有事务,test2 无事务,事务生效
  • test1 有事务,test2 有事务,事务生效
  • test1 无事务,test2 有事务,事务不生效
  • test1 无事务,test2 无事务,事务不生效

3、异常类型错误

public class ServiceA {
    @Transactional
    public void add(UserModel userModel) throws Exception {
        try {
             addUser();
             addTeacher();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new Exception(e);
        }
    }
}

可以看到发生异常时,被 try-catch 补获并抛出 exception 异常,但是因为 spring 事务,默认情况下只会回滚 RuntimeException(运行时异常)和 Error(错误),对于普通的 Exception(非运行时异常),它不会回滚。所以一般都要求 Transactional 指定 rollbackfor。

4、抛出的异常被吃了

@Service
public class ServiceA {

    @Transactional(rollbackFor=Exception.class)
    public void add(UserModel userModel) throws Exception {
        try {
             addUser();
             addTeacher();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}

上面异常被 try-catch 吃掉了,且没有往外抛异常,所以这个时候事务是无法回滚。

5、数据表本身是不支持事务,导致事务失效

1、实例
  如果使用MySQL且存储引擎是MyISAM,则事务是不起作用的,原因是MyIASM不支持事务。
2、解决
  数据表可以改为InnoDB存储引擎,支持事务

6.多线程调用

 这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,add方法也回滚是不可能的。

7.错误的传播特性

        @Transactional 的 propagation 参数设置不对,spring 目前支持 7 种传播特性。

目前只有这三种传播特性才会创建新事务:REQUIRED,REQUIRES_NEW,NESTED。

8.未被spring容器管理

视频推荐:江南一点雨

01.什么是事务_哔哩哔哩_bilibili

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值