事务的属性

奋斗吧
奋斗吧
擅长邻域:未填写

标签: 事务的属性 Java博客 51CTO博客

2023-06-14 18:24:23 199浏览

事务的属性,代码写在course28中1. 事务属性包括哪些  1231.1 事务中的重点属性: 123● 事务传播行为● 事务隔离级别● 事务超时● 只读事务● 设置出现哪些异常回滚事务● 设置出现哪些异常不回滚事务2. 事务传播行为  1232.1 什么是事

代码写在course28中

1. 事务属性包括哪些  123

事务的属性_隔离级别

1.1 事务中的重点属性: 123

● 事务传播行为

● 事务隔离级别

● 事务超时

● 只读事务

● 设置出现哪些异常回滚事务

● 设置出现哪些异常不回滚事务

2. 事务传播行为  123

2.1 什么是事务的传播行为?  123

在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。

事务传播行为在spring框架中被定义为枚举类型:

事务的属性_事物的属性_02

2.2 一共有七种传播行为:

● REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】

● SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】

● MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】

● REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】

● NOT_SUPPORTED:以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】

● NEVER:以非事务方式运行,如果有事务存在,抛出异常【不支持事务,存在就抛异常】

● NESTED:如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】

2.2.1 事务传播行为REQUIRED   124-125

在代码中设置事务的传播行为:

AccountServiceImpl实现类中的save方法,去调用AccountServiceImpl2中的save方法

将AccountServiceImpl2中的save方法的事务传播行为设置为@Transactional(propagation = Propagation.REQUIRED)   (这里解释AccountServiceImpl实现类中的save方法为社么也要添加事务传播行为REQUIRED,目的就是开启这个方法的事务,就算是我们不指定,我们的AccountServiceImpl上面有@Transactional注解 //针对这个类开启事务 ,也就是说这个类里的所有方法都会开启事务 )

AccountServiceImpl类
@Resource(name = "accountService2")
    private AccountService accountService;

    //研究事务传播行为  124
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(Account act) {

        // 这里调用dao的insert方法。
        accountDao.insert(act); // 保存act-003账户

        // 创建账户对象
        Account act2 = new Account("act-004", 1000.0);
        try {
            //这里调用AccountServiceImpl2中的save方法
            accountService.save(act2); // 保存act-004账户
        } catch (Exception e) {

        }
        // 继续往后进行我当前1号事务自己的事儿。
    }
AccountServiceImpl2类

AccountServiceImpl2中的save方法中模拟异常

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * AccountService接口第二个实现类  124
 **/
@Service("accountService2")
public class AccountServiceImpl2 implements AccountService {

    @Resource(name = "accountDao")
    private AccountDao accountDao;

    @Override
    public void transfer(String fromActno, String toActno, double money) {

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void save(Account act) {
        accountDao.insert(act);
        // 模拟异常  125
        String s = null;
        s.toString();

        // 事儿没有处理完,这个大括号当中的后续也许还有其他的DML语句。
    }
}
可以编写程序测试一下传播行为:
//研究事务传播行为  124-125
    @Test
    public void testPropagation() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        // 获取1号service对象
        AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
        Account act = new Account("act-003", 1000.0);
        accountService.save(act);
    }

事务的属性_spring_03

事务的属性_隔离级别_04

总结

没有增加act-003和act-004,说明事务控制住了,正面证明了AccountServiceImpl实现类中的save方法和AccountServiceImpl2中的save方法,在添加事务传播行为设置为@Transactional(propagation = Propagation.REQUIRED)时,用的是一个事务,方法1中如果有try—catch也捕捉不到异常,因为两个方式是一个事务,但凡遇到异常就回滚

再次强调REQUIRED:支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】

2.2.2 事务传播行为REQUIRES_NEW   126

开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】

我们将之前的代码AccountServiceImpl2中的save方法事务传播行为设置为 @Transactional(propagation = Propagation.REQUIRES_NEW)并且模拟异常

@Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save(Account act) {
        accountDao.insert(act);
        // 模拟异常  125
        String s = null;
        s.toString();

        // 事儿没有处理完,这个大括号当中的后续也许还有其他的DML语句。
    }

事务的属性_spring_05

总结

可以和明显的看出act-003添加成功,但是act-004没有添加成功,说明AccountServiceImpl中的save方法事务和AccountServiceImpl2中的save方法事务时不同的是两个事务,也就是说当方法2发生异常,方法1中如果有try—catch的话会捕捉到异常进而不会影响自己的代码执行提交,方法1中的事务不会管方法2,但是如果我们的方法1中没有设置try—catch,就不会捕捉方法2的异常,一样会报错回滚,进而二者都不成功

REQUIRES_NEW不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起

3. 事务的隔离级别  127

事务的属性_事物的属性_06

事务隔离级别类似于教室A和教室B之间的那道墙,隔离级别越高表示墙体越厚。隔音效果越好。

3.1 数据库中读取数据存在的三大问题:(三大读问题)127

脏读:读取到没有提交到数据库的数据,叫做脏读。

不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。

幻读:读到的数据是假的。

3.2 事务隔离级别包括四个级别:  127

读未提交:READ_UNCOMMITTED

这种隔离级别,存在脏读问题,所谓的脏读(dirty read)表示能够读取到其它事务未提交的数据。

读提交:READ_COMMITTED

解决了脏读问题,其它事务提交之后才能读到,但存在不可重复读问题。

可重复读:REPEATABLE_READ

解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据一直都是一样的。但存在幻读问题。

序列化:SERIALIZABLE 

解决了幻读问题,事务排队执行。不支持并发。

大家可以通过一个表格来记忆:

隔离级别

脏读

不可重复读

幻读

读未提交

读提交

可重复读

序列化

3.3 在Spring代码中如何设置隔离级别?  128

隔离级别在spring中以枚举类型存在:

事务的属性_隔离级别_07

@Transactional(isolation = Isolation.READ_COMMITTED)

3.4 测试事务隔离级别:READ_UNCOMMITTED   129

怎么测试:一个service负责插入,一个service负责查询。负责插入的service要模拟延迟。

IsolationService1负责查询

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

/**
 * 事务的隔离级别   129
 * 测试事务隔离级别:READ_UNCOMMITTED 和 READ_COMMITTED
 **/
@Service("i1") //添加进spring管理
public class IsolationService1 {

    @Resource(name = "accountDao") //注入
    private AccountDao accountDao;

    // 1号
    // 负责查询
    // 当前事务可以读取到别的事务没有提交的数据。
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    // 对方事务提交之后的数据我才能读取到。
    //@Transactional(isolation = Isolation.READ_COMMITTED)
    public void getByActno(String actno) {
        Account account = accountDao.selectByActno(actno);
        System.out.println("查询到的账户信息:" + account);
    }

}

IsolationService2负责插入

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

/**
 * 事务的隔离级别   129
 * 测试事务隔离级别:READ_UNCOMMITTED 和 READ_COMMITTED
 **/
@Service("i2")
public class IsolationService2 {

    @Resource(name = "accountDao")
    private AccountDao accountDao;

    // 2号
    // 负责insert
    @Transactional
    public void save(Account act) throws IOException {

       accountDao.insert(act);

        // 睡眠一会
        try {
            Thread.sleep(1000 * 20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

测试

//事务的隔离级别   129
    //测试事务隔离级别:READ_UNCOMMITTED 和 READ_COMMITTED
    @Test
    public void testIsolation1() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        IsolationService1 i1 = applicationContext.getBean("i1", IsolationService1.class);
        i1.getByActno("act-004");
    }

    //事务的隔离级别   129
    //测试事务隔离级别:READ_UNCOMMITTED 和 READ_COMMITTED
    @Test
    public void testIsolation2() throws IOException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        IsolationService2 i2 = applicationContext.getBean("i2", IsolationService2.class);
        Account act = new Account("act-004", 1000.0);
        i2.save(act);
    }

很明显在IsolationService2还没提交时,我们的IsolationService1就查到了数据

强调READ_UNCOMMITTED时读未提交

事务的属性_spring_08

3.5 测试事务隔离级别READ_COMMITTED

将设置为读提交READ_COMMITTED   即@Transactional(isolation = Isolation.READ_COMMITTED)

对方事务提交之后的数据我才能读取到。

@Transactional(isolation = Isolation.READ_COMMITTED)
    public void getByActno(String actno) {
        Account account = accountDao.selectByActno(actno);
        System.out.println("查询到的账户信息:" + account);
    }
package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.pojo.Account;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

/**
 * 事务的隔离级别   129
 * 测试事务隔离级别:READ_UNCOMMITTED 和 READ_COMMITTED
 **/
@Service("i2")
public class IsolationService2 {

    @Resource(name = "accountDao")
    private AccountDao accountDao;

    // 2号
    // 负责insert
    @Transactional
    public void save(Account act) throws IOException {

       accountDao.insert(act);

        // 睡眠一会
        try {
            Thread.sleep(1000 * 20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

测试

//事务的隔离级别   129
    //测试事务隔离级别:READ_UNCOMMITTED 和 READ_COMMITTED
    @Test
    public void testIsolation1() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        IsolationService1 i1 = applicationContext.getBean("i1", IsolationService1.class);
        i1.getByActno("act-004");
    }

    //事务的隔离级别   129
    //测试事务隔离级别:READ_UNCOMMITTED 和 READ_COMMITTED
    @Test
    public void testIsolation2() throws IOException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        IsolationService2 i2 = applicationContext.getBean("i2", IsolationService2.class);
        Account act = new Account("act-004", 1000.0);
        i2.save(act);
    }

结果显示报错,因为IsolationService2还没提交,IsolationService1根本查不到数据,就会报错

事务的属性_事务隔离级别_09

好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695