事物:在一个业务流程中,通常需要多条DML(insert delete update)语句共同联合才能完成的,为了保证数据的安全,多条DML语句都必须同时成功,,或同时失败。
事物的四个处理过程:开启事务、执行核心业务代码、提交事务、回滚事务
事务的四个特性:
原子性:事务是最小工作单位,不可再分割
一致性:事务要么同时成功,要么同时失败。事务前和事务后的总量不变
隔离性:事务和事务之间有隔离性,互不干扰
持久性:持久性是事务结束的标志
Spring实现事务的两种方式
编程式事务:编写代码来实现事务的管理
声明式事务*:基于注解的方式、基于xml配置方式
如有错误请指正,谢谢
以银行账户转账为案例
数据库表
spring6整合mybatis
pom.xml配置文件
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">
4.0.0
org.example
spring6-transaction-bank
1.0-SNAPSHOT
17
17
UTF-8
jar
repository.spring.milestone
Spring Milestone Repository
https://repo.spring.io/milestone
jakarta.annotation
jakarta.annotation-api
2.1.0
org.springframework
spring-context
6.0.0-M2
org.springframework
spring-jdbc
6.0.0-M2
mysql
mysql-connector-java
8.0.17
org.mybatis
mybatis
3.5.11
org.mybatis
mybatis-spring
3.0.1
com.alibaba
druid
1.2.15
org.springframework
spring-test
6.0.0-M2
org.junit.jupiter
junit-jupiter-api
5.8.2
test
src/main/java
**/*.properties
**/*.xml
false
jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://IP:3306/mysql
jdbc.user= root
jdbc.password= xxxxxx
mybatis-config.xml配置文件
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
SpringConfig.xml配置文件
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
BankDao接口
public interface BankDao {
/**
* 根据账号查询账户信息
*/
List selectAll();
/**
* 根据actno查询账户信息
* @param actno
* @return
*/
Bank selectByActno(String actno);
/**
* 更新账户信息
* @param act
* @return
*/
int update(Bank act);
}
BankDao.xml
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
select * from bank
select actno,balance from bank where actno = #{actno}
update bank set balance=#{balance} where actno =#{actno}
pojo实体类
package com.qgs.pojo;
/**
* @author QGS
* @version 1.0.0
* @date 2023年03月24日 16:36:58
* @packageName com.qgs.pojo
* @className Bank
* @describe TODO
*/
public class Bank {
private String actno;
private Double balance;
public Bank() {
}
public Bank(String actno, Double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Bank{" +
"actno='" + actno + '\'' +
", balance=" + balance +
'}';
}
}
BankService 接口
public interface BankService {
/**
* 转账方法
* @param fromActno 转出账户
* @param toActno 转入账户
* @param money 金额
*/
void transfer(String fromActno ,String toActno ,double money);
}
BankService 接口实现类
package com.qgs.service;
import com.qgs.dao.BankDao;
import com.qgs.pojo.Bank;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @author QGS
* @version 1.0.0
* @date 2023年03月25日 11:15:24
* @packageName com.qgs.service
* @className BankServiceImpl
* @describe TODO
*/
@Service("bankServiceImpl")
//@Transactional
public class BankServiceImpl implements BankService{
@Resource(name ="bankDao")
private BankDao bankDao;
@Override
//@Transactional默认propagation = Propagation.REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(String fromActno, String toActno, double money) {
//查询转出账户余额
Bank fromBank = bankDao.selectByActno(fromActno);
if (fromBank.getBalance()
throw new RuntimeException("余额不足");
}
//查询转入账户余额
Bank toBank = bankDao.selectByActno(toActno);
//将内存中转出与转入账户余额修改
fromBank.setBalance(fromBank.getBalance() - money);
toBank.setBalance(toBank.getBalance() + money);
//更新数据库
int count = bankDao.update(fromBank);
int count2 = bankDao.update(toBank);
count +=count2;
if (count !=2){
//回滚事务,转账失败
throw new RuntimeException("失败");
}
}
}
@Test
public void BankTest(){
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("SpringConfig.xml");
BankService bankService = applicationContext.getBean("bankServiceImpl", BankService.class);
try {
bankService.transfer("act01","act2",300);
}catch (Exception e){
e.printStackTrace();
}
}
事务的传播行为
事务的传播行为
事务传播行为在spring框架中被定义为枚举类型。
事务的传播行为有7种:
REQUIRED:支持当前事务,如果不存在就新建一个(默认模式)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常。
REQUIRES_NEW:开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起。
NOT_SUPPORTED:以非事务方式运行,如果事务存在,挂起当前事务。
NEVER:以非事务方式运行,如果有事务存在,抛出异常。
NESTED:如果当前争优一个事务在进行中,则该方法应当运行一个嵌套事务中。被嵌套的事务可以单独于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED。
数据库中读取数据存在的三大问题
脏读:读取到没有提交的数据库的数据。
不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。
幻读:读到的数据是假的。(事务并发,一定存在幻读)
spring事务隔离级别
数据库中读取数据存在的三大读问题
脏读:读取到没有提交的数据库的数据。
不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。
幻读:读到的数据是假的。(事务并发,一定存在幻读)
事务隔离级别的四个级别:
读未提交:READ_UNCOMMITTEN
这种隔离级别:存在脏读问题,所谓的脏读(dirty read)表示能够读取到其他事务未提交的数据。
读提交:READ_COMMITTED (oracle)
解决了脏读问题,其他事务提交之后才能读到,但存在不可重复读问题
可重复读:REPEATABLE_READ (MYSQL)
解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据移植都是一样的。但存在幻读问题。
序列化:SERIALLZABLE
解决了幻读问题,事务排毒执行。不支持并发。
Spring事务超时
Spring事务超时
@Transactional(timeout = 10) 表示设置事务的超时时间为10秒。
超过10秒如果该事务所有的DML语句还没有执行完毕,最终选择回滚。
在当前事务中,最后一条DML语句执行之前的时间。最后一条DML语句后面还有很多业务逻辑(非DML语句),这些业务代码执行的时间不被记入超时时间
默认值为:timeout = -1 、表示没有时间限制。
Spring启动只读事务
Spring启动只读事务
@Transactional(readOnly = true) 作用:启动Spring优化策略,提高select语句执行效率
Spring事务设置异常回滚问题
Spring事务设置异常回滚问题
设置RuntimeException异常时回滚
@Transactional(rollbackFor = RuntimeException.class)
设置NullPointerException异常不回滚
@Transactional(noRollbackFor = NullPointerException.class)