代码写在course28中1.事务属性包括哪些123
1.1事务中的关键属性:123●事务传播行为
●事务隔离等级
●事务超时
●只读事务
●什么异常的回滚事务设置?
●设置什么异常不回滚事务?
2.事务传播行为1232.1是什么?123a()方法和b()方法在service类中,a()方法上有事务,b()方法上也有事务。当B()方法在a()方法实施过程中被调用时,事务是如何传递的?合并到事务中?还是开始新的事务?这就是事务传播行为。
Spring框架中的事务传播行为被定义为枚举类型:
2.2共有七种传播行为:●REQUIRED:支持当前事务,如果不存在,新建一个(默认)【如果没有,就新建,如果有,就加入】
●SUPPORTS:支持当前事务。如果当前没有事务,则以非事务的形式执行。【有就加入,没有就不管】
●MANDATORY:如果目前没有正在发生的事务,必须在一个事务中运行【有就加,没有就抛异常】
●REQUIRES_NEW:开始一个新的事务,如果一个事务已经存在,就把它挂起来【不管有没有,直接开新事务,开新事务和之前的事务没有嵌套关系,之前的事务都挂了】
●NOT_SUPPORTED:以非事务的形式运行,如有事务,挂起当前事务【不支持事务,存在就挂】
●NEVER:以非事务的形式运行,如有事务,抛出异常【不支持事务,存在就抛异常】
●NESTED:如果目前正在进行事务,则该方法应在嵌套事务中运行。嵌套事务可以独立于外部事务提交或回滚。如果没有外部事务,行为就像REQUIRED。【如果有事务,在此事务中嵌套另一个完全独立的事务,可以独立提交和回滚。没有事情就像REQUIRED。】
2.2.REQUIRED124-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号事务。 }
Accountserviceipl2类在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 Accountserviceimplell2 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.REQUIRES_NEW126开始一个新的事务,如果一个事务已经存在,就把它挂起来【不管有没有,直接开新事务,开新事务和之前的事务没有嵌套关系,之前的事务都挂了】
我们将之前代码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
这种隔离水平存在脏读问题。所谓脏读(dirtyread)表示可以读取其他事务未提交的数据。
读提交:READ_COMMITTED
只有在提交其他事务后才能解决脏阅读问题,但存在不可重复阅读的问题。
可重复读:REPEATABLE_READ
如果解决了不可重复读取的问题,可以达到可重复读取的效果。只要目前的事务没有结束,读取的数据总是一样的。但是有幻读的问题。
序列化:SERIALIZABLE
解决幻读问题,排队执行事务。不支持并发。
可以通过表格记忆:
隔离级别
脏读
不可重复读
幻读
读未提交
有
有
有
读提交
无
有
有
可重复读
无
无
有
序列化
无
无
无
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_UNCOMITED
3.5测试事务隔离级READ_COMMITTED读取和提交READ_COMMITED@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根本找不到数据,就会报错