Spring事务管理
1、事务
-
什么是事务
一组操作要么都成功,要么都失败
-
事务的特性
ACID
- 原子性,一组操作是一个整体,要么全成功,要么全失败,不可分隔
- 一致性:事务前后数据保持一致
- 隔离性:多个事务并发访问数据库,并发事务相互隔离
- 持久性:事务一旦提交不能修改
-
隔离问题?
- 脏读:一个事务读到另一个事务没有提交的数据
- 不可重复读:一个事务读到另一个数为已经提交的数据(update)
- 幻读:一个事务读到另一个数为已经提交的数据(insert)
不可重复读指对数据进行了修改
幻读指添加了一条数据
-
隔离级别
- 读未提交(read uncommitted):一个事务读到另一个事务没有提交的数据
- 读已提交(read committed):一个事务读到另一个事务已经提交的数据,解决了一个问题(脏读)
- 可重复读(repeatable read):在一个事务中读到的数据重复的。解决2个问题(脏读、不可重复读)
- 串行化(Serializable):单事务,解决3个问题(脏读、不可重复读、幻读)
-
对比
- 性能:read uncommitted > read committed > repeatable read > Serializable
- 安全:read uncommitted < read committed < repeatable read < Serializable
2、事务管理
2.1、事务的传播行为
事务的传播行为有7种
传播行为 | 描述 |
---|---|
propagation_required | 如果当前没有事务,就创建一个,如果有就使用当前的 |
propagation_supports | 支持当前事务,如果没有事务,就以非事务执行 |
propagation_mandatory | 使用当前事务,如果没有事务,就抛出异常 |
propagation_requires_new | 新建事务,如果当前有事务,就挂起 |
propagation_not_supported | 非事务执行,如果当前有事务,就挂起 |
propagation_never | 以非事务执行,如果当前有,就抛出异常 |
propagation_nested | 当前事务存在,在嵌套事务中执行,没有就用propagation_required |
2.2、事务的隔离级别
隔离级别
隔离级别 | 描述 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 读未提交 |
ISOLATION_READ_COMMITTED | 读已提交 |
ISOLATION_REPEATABLE_READ | 读可重复 |
ISOLATION_SERIALIZABLE | 穿行化 |
2.3、只读
表示只能对数据库进行读取操作,不能对数据库进行增删改操作
2.4、基本操作(注解)
2.4.1、需求
完成转账
2.4.2、实现
表结构
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
money FLOAT
);
表数据
配置文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db1
jdbc.username=root
jdbc.password=root
Configuration类
package com.czxy.demo11_transaction.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import tk.mybatis.spring.annotation.MapperScan;
import javax.sql.DataSource;
@Configuration // 标明配置类
@ComponentScan("com.czxy.demo11_transaction") // 设置组件扫描路径
@PropertySource("classpath:db.properties") //加载配置文件
@MapperScan("com.czxy.demo11_transaction.mapper") //mapper扫描路径 使用的通用mapper 需要导入的是tk.mybatis.包下的
@EnableTransactionManagement //开启事务
public class SpringConfig {
@Value("${jdbc.driver}")
private String driver; //驱动
@Value("${jdbc.url}")
private String url; //路径
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
//配置连接池
@Bean
public DataSource dataSource(){
DruidDataSource source = new DruidDataSource();
source.setDriverClassName(driver);
source.setUrl(url);
source.setUsername(username);
source.setPassword(password);
return source;
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
//1 创建工厂
// 1.通过工厂bean创建对象,最后需要调用 getObject()获得具体的对象
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// 1.1 设置数据源
factoryBean.setDataSource(dataSource);
// 1.2 设置别名包扫描
factoryBean.setTypeAliasesPackage("com.czxy.sm.domain");
// 1.3 全局配置:驼峰映射
org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
config.setMapUnderscoreToCamelCase(true);
factoryBean.setConfiguration(config);
// 返回SqlSessionFactory
return factoryBean.getObject();
}
}
service接口
public interface UserService {
/**
*
* @param outId 输出账号
* @param intId
* @param money
*/
void change(Integer outId, Integer intId, float money);
}
service实现类
@Service
@Transactional //开启事务
public class UserServiceImpl implements UserService {
@Resource
private AccountMappper mapper;
public void change(Integer outId, Integer intId, float money) {
Account intAccount = mapper.selectByPrimaryKey(intId);
intAccount.setMoney(intAccount.getMoney() + money);
mapper.updateByPrimaryKey(intAccount);
Account outAccount = mapper.selectByPrimaryKey(outId);
outAccount.setMoney(outAccount.getMoney() - money);
mapper.updateByPrimaryKey(outAccount);
}
}
javaBean
//数据库表名未account
public class Account {
@Id
private Integer id;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
}
测试类
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DemoTest {
@Resource
private UserService service;
@Test
public void test01(){
service.change(1,2,100);
System.out.println("成功");
}
}
运行后
错误的情况下
在service方法里面加上异常
public class UserServiceImpl implements UserService {
@Resource
private AccountMappper mapper;
public void change(Integer outId, Integer intId, float money) {
Account intAccount = mapper.selectByPrimaryKey(intId);
intAccount.setMoney(intAccount.getMoney() + money);
mapper.updateByPrimaryKey(intAccount);
int i =1/0;
Account outAccount = mapper.selectByPrimaryKey(outId);
outAccount.setMoney(outAccount.getMoney() - money);
mapper.updateByPrimaryKey(outAccount);
}
}
运行后
数据库的数据没有改变。
2.5、小结
-
在配置类上加上@EnableTransactionManagement
- 并添加一个DataSourceTransactionManager的bean
-
在需要事务的实现类上加上@Transactional
-
@Transactional的参数
-
@Transactional( readOnly = false, //只读 timeout = -1, //超时 isolation = Isolation.DEFAULT, //隔离级别 propagation = Propagation.REQUIRED) //传播行为
-
一个DataSourceTransactionManager的bean
-
在需要事务的实现类上加上@Transactional
-
@Transactional的参数
-
@Transactional( readOnly = false, //只读 timeout = -1, //超时 isolation = Isolation.DEFAULT, //隔离级别 propagation = Propagation.REQUIRED) //传播行为
-