声明式事物管理
什么是事物管理
事物的概念
事务管理是指对数据库操作的一系列相关操作进行统一管理,保证这些操作要么全部成功执行,要么全部失败回滚,从而确保数据的完整性和一致性。
在数据库操作中,一个事务通常包括一系列的数据库操作(如增、删、改),这些操作要么全部执行成功并提交到数据库中,要么出现错误时进行回滚,使得数据库状态可以回到事务开始前的状态。
事务的ACID原则
事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则。
1.原子性:一个事务已经是一个不可再分割的工作单位。
2.一致性: 指事务执行前后,数据库的完整性约束没有被破坏。
3.隔离性: 指多个事务并发执行时,事务之间应该相互隔离,一个事务的执行不应该影响其他事务的执行。
4.持久性: 指一旦事务提交,其所做的修改将会永久保存在数据库中,即使系统发生故障,这些修改的数据也不 会丢失。
Spring 中的事物隔离级别
隔离级别由低到高【读未提交】=>【读已提交】=>【可重复读】=>【序列化操作】
隔离级别 | 说明 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
ISOLATION_DEFAULT | spring默认数据库的隔离级别 | – | – | – |
ISOLATION_READ_UNCOMMITTED | 读未提交 | √ | √ | √ |
ISOLATION_READ_COMMITTED | 读已提交 | × | √ | √ |
ISOLATION_REPEATABLE_READ | 可重复读 | × | × | √ |
ISOLATION_SERIALIZABLE | 序列化操作 | × | × | × |
如果没有事物隔离级别就会产生以下问题
1.脏读: 是数据库事务隔离级别中可能出现的问题之一,指的是一个事务读取了另一个事务未提交的数据。
2.不可重复读: 是指在数据库事务隔离级别中,同一事务中多次查询同一条数据,但在这个过程中却得到了不同 的结果。
3.幻读: 是指在数据库事务隔离级别中,一个事务在读取某个范围的记录时,另一个事务在该范围内插入了新的 记录,导致第一个事务在接下来的读取中出现了之前不存在的记录,就好像发生了幻觉一样。
声明式事物概念
编程式事物
在提及声明式事物是我们需要了解另一项事物,编程式事物: 编程式事务是指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。在 Java 中,通常使用事务管理器来实现编程式事务 。
优点:灵活性高 可以按照自己的需求来控制事务的粒度、模式等 。
缺点:细节都需要程序员自己来完成,繁琐;复用性不高,事物控制代码多了以后,会影响大妈的可读性和可维护性。
声明式事物
声明式事务是指使用注解或 XML 配置的方式来控制事务的提交和回滚。
开发者只需要添加配置即可, 具体事务的实现由第三方框架实现,避免我们直接进行事务操作!
使用声明式事务可以将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性。
声明式事物的实现(注解方式)
创建数据库studb,在数据库中新建students表
#
# Structure for table "students"
#
DROP TABLE IF EXISTS `students`;
CREATE TABLE `students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`gender` varchar(10) NOT NULL,
`age` int(11) DEFAULT NULL,
`class` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
#
# Data for table "students"
#
INSERT INTO `students` VALUES (1,'张三','男',99,'高中一班'),(2,'李四','男',19,'高中二班'),(3,'王五','女',18,'高中一班'),(4,'赵六','女',20,'高中三班'),(5,'刘七','男',19,'高中二班'),(6,'陈八','女',18,'高中一班'),(7,'杨九','男',20,'高中三班'),(8,'吴十','男',19,'高中二班');
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.6</version>
</dependency>
</dependencies>
外部配置文件(jdbc.properties)
jdbc.url=jdbc:mysql://localhost:3306/studb
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=root
配置文件
@Configuration
@ComponentScan("org.example")
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement
public class JavaConfig {
@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 dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public TransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
dao和service层(注:@Transactional(readOnly=true)表示只读模式,不能进行数据的更改 )
@Transactional(timeout=3)表示在提交后3秒后没能提交成功,事物将回滚
@Transactional( rollbackFor = 异常类.class )表示遇到此异常类将发生回滚)
@Repository
public class StudentDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void updateNameById(String name,Integer id){
String sql = "update students set name = ? where id = ? ;";
int rows = jdbcTemplate.update(sql, name, id);
}
}
@Service
public class StudentService {
@Autowired
private StudentDao studentDao;
@Transactional
public void updateNameById(){
studentDao.updateAgeById(100,1);
System.out.println("-----------");
studentDao.updateNameById("张三",1);
}
}
测试
@SpringJUnitConfig(classes = DataSourceConfig.class)
public class TxTest {
@Autowired
private StudentService studentService;
@Test
public void test(){
studentService.updateNameById();
}
}
参考于尚硅谷新版SSM框架全套视频教程,Spring6+SpringBoot3最新SSM企业级开发