什么是事务
事务(Transaction)是指作为一个逻辑工作单元执行的一系列操作,这些操作要么全部成功完成,要么全部失败回滚。事务是数据库管理系统中保证数据一致性和完整性的重要机制。
事务具有以下四个特性,通常被称为 ACID 特性:
- 原子性(Atomicity):事务是一个原子操作单位,不可分割。它要么全部执行成功,要么全部撤销,没有中间状态。如果事务的所有操作都成功执行,就会被提交;如果有任何一个操作失败,就会被回滚到事务开始前的状态。
- 一致性(Consistency):在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏。事务执行过程中的中间状态不会被其他事务所见,只有在事务提交后才会对其他事务可见。
- 隔离性(Isolation):多个事务并发执行时,每个事务的执行都应该像是在独立的环境中执行,互相之间不会干扰。每个事务都应该感知不到其他事务的存在,避免出现读取未提交数据、脏读、不可重复读和幻读等并发问题。
- 持久性(Durability):一旦事务被提交,对数据库的修改将会永久保存,即使系统发生故障也不会丢失。持久性确保了数据的可靠性和持久可追溯性。
如何控制事务
JDBC: Connection.setAutoCommit(false); Connection.commit(); Connection.rollback(); MyBatis: SqlSession.commit(); SqlSession.rollback();
控制事务的底层都是通过连接对象(Connection)来控制事务的,Mybatis 的 SqlSession 底层也是对 Connection 的封装。
开发 Demo
引入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.1.4.RELEASE</version> </dependency>
原始对象
world.xuewei.service.AccountService
package world.xuewei.service; import world.xuewei.entity.Account; /** * 账户服务 * * @author 薛伟 * @since 2023/10/23 16:23 */ public interface AccountService { /** * 账户注册 */ void register(Account account); }
world.xuewei.service.AccountServiceImpl
package world.xuewei.service; import org.springframework.transaction.annotation.Transactional; import world.xuewei.dao.AccountDao; import world.xuewei.entity.Account; /** * 账户服务实现类 * * @author 薛伟 * @since 2023/10/23 16:24 */ @Transactional public class AccountServiceImpl implements AccountService { private AccountDao accountDao; /** * 账户注册 */ @Override public void register(Account account) { // 注册逻辑编写 accountDao.insert(account); } public AccountDao getAccountDao() { return accountDao; } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } }
@Transactional 注解可以加在指定方法或者类上。
Spring 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://**.**.*.*/learn?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="*****"/> </bean> <!-- 配置 SqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 指定所有实体所在的包 --> <property name="typeAliasesPackage" value="world.xuewei.entity"/> <property name="mapperLocations"> <list> <!-- 将所有的 Mapper 的配置文件放在 resources/mappers 目录下 --> <value>classpath:mappers/*Mapper.xml</value> </list> </property> </bean> <!-- 配置 MapperScannerConfigurer --> <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> <!-- 指定所有 dao 所在的包 --> <property name="basePackage" value="world.xuewei.dao"/> </bean> <!-- 注册账户服务 --> <bean id="accountService" class="world.xuewei.service.AccountServiceImpl"> <!-- 注入账户 Dao,Dao 的 Bean 名字为对应 Dao 类名的首单词首字母小写--> <property name="accountDao" ref="accountDao"/> </bean> <!-- 配置事务管理器 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 组装切面 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> </beans>
整个 Spring 事务控制的底层就是使用了 AOP。
tx:annotation-driven
可以通过配置proxy-target-class="true"
强制底层在创建代理的时候使用 Cglib 方式
测试程序
package world.xuewei; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; import world.xuewei.entity.Account; import world.xuewei.service.AccountService; /** * @author 薛伟 * @since 2023/9/14 20:51 */ public class DaoTest { private ClassPathXmlApplicationContext context; @Before public void before() { // 指定配置文件,创建 Spring 工厂 context = new ClassPathXmlApplicationContext("/applicationContext.xml"); } /** * 测试事务 */ @Test public void test2() { AccountService service = context.getBean("accountService", AccountService.class); Account account = new Account(2, "张三", "123456"); service.register(account); } }
正常提交
异常回滚