代码写在course28中
1. 事务属性包括哪些 123
1.1 事务中的重点属性: 123
● 事务传播行为
● 事务隔离级别
● 事务超时
● 只读事务
● 设置出现哪些异常回滚事务
● 设置出现哪些异常不回滚事务
2. 事务传播行为 123
2.1 什么是事务的传播行为? 123
在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。
事务传播行为在spring框架中被定义为枚举类型:
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);
}
总结
没有增加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语句。
}
总结
可以和明显的看出act-003添加成功,但是act-004没有添加成功,说明AccountServiceImpl中的save方法事务和AccountServiceImpl2中的save方法事务时不同的是两个事务,也就是说当方法2发生异常,方法1中如果有try—catch的话会捕捉到异常进而不会影响自己的代码执行提交,方法1中的事务不会管方法2,但是如果我们的方法1中没有设置try—catch,就不会捕捉方法2的异常,一样会报错回滚,进而二者都不成功
REQUIRES_NEW不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起
3. 事务的隔离级别 127
事务隔离级别类似于教室A和教室B之间的那道墙,隔离级别越高表示墙体越厚。隔音效果越好。
3.1 数据库中读取数据存在的三大问题:(三大读问题)127
脏读:读取到没有提交到数据库的数据,叫做脏读。
不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。
幻读:读到的数据是假的。
3.2 事务隔离级别包括四个级别: 127
读未提交:READ_UNCOMMITTED
这种隔离级别,存在脏读问题,所谓的脏读(dirty read)表示能够读取到其它事务未提交的数据。
读提交:READ_COMMITTED
解决了脏读问题,其它事务提交之后才能读到,但存在不可重复读问题。
可重复读:REPEATABLE_READ
解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据一直都是一样的。但存在幻读问题。
序列化:SERIALIZABLE
解决了幻读问题,事务排队执行。不支持并发。
大家可以通过一个表格来记忆:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交 | 有 | 有 | 有 |
读提交 | 无 | 有 | 有 |
可重复读 | 无 | 无 | 有 |
序列化 | 无 | 无 | 无 |
3.3 在Spring代码中如何设置隔离级别? 128
隔离级别在spring中以枚举类型存在:
@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时读未提交
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根本查不到数据,就会报错
标签:事务,READ,Account,act,import,save,属性 From: https://blog.51cto.com/u_15784725/6476779