1. 事务的基本概念
- 定义:事务是一组数据库操作的集合,这些操作要么全部成功执行,要么全部不执行。它是为了保证数据的一致性和完整性。例如,在一个银行转账系统中,从一个账户扣款和向另一个账户收款这两个操作应该作为一个事务来处理。如果扣款成功但收款失败,就会导致数据不一致,而事务可以防止这种情况的发生。
- 事务的特性(ACID)
- 原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么全部执行,要么全部不执行。就像在上面的银行转账例子中,转账这个事务包含了两个操作,从账户A扣款和给账户B收款,这两个操作必须同时成功或者同时失败。
- 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏。在转账事务完成后,整个银行系统的资金总额应该保持不变,这就是保证了数据的一致性。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。例如,同时有多个用户进行转账操作,每个转账事务应该相互独立,互不干扰。
- 持久性(Durability):一旦事务提交,它对数据库中数据的改变就应该是永久性的。即使系统出现故障,已经提交的事务的数据修改也不会丢失。
2. Spring Boot中的事务管理
- 编程式事务管理
-
方式:通过编写代码来手动控制事务的开启、提交和回滚。在Spring Boot中,可以使用
PlatformTransactionManager
接口来实现编程式事务管理。例如,以下是一个简单的示例代码:import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; @Service public class MyService {@Autowiredprivate PlatformTransactionManager transactionManager;public void doSomethingTransactional() {TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());try {// 业务逻辑代码,例如数据库操作//...transactionManager.commit(status);} catch (Exception e) {transactionManager.rollback(status);}} }
-
缺点:这种方式代码比较繁琐,并且会将事务管理的代码和业务逻辑代码紧密耦合在一起,不利于代码的维护和扩展。
-
- 声明式事务管理(推荐)
-
方式:通过在方法或者类上添加注解来实现事务管理,这种方式更加简洁,将事务管理的代码和业务逻辑代码分离。在Spring Boot中,通常使用
@Transactional
注解来实现声明式事务管理。例如:import org.springframework.transaction.annotation.Transactional; import org.springframework.stereotype.Service; @Service @Transactional public class MyService {public void doSomethingTransactional() {// 业务逻辑代码,例如数据库操作// 这里的方法会自动被事务管理,当方法执行过程中出现异常时会自动回滚事务} }
@Override@Transactional(propagation=Propagation.REQUIRED)public void saveUser(String userName,String userPwd){User user1=new User();user1.setUserName(userName);user1.setUserPwd(userPwd);userDao.saveUser(user1);userDao.delUserById(2);}
-
传播行为:
@Transactional
注解还可以指定事务的传播行为。传播行为定义了在一个已经存在事务的上下文中,事务方法应该如何执行。@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加⼊事务, 没有的话新建⼀个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个⽅法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建⼀个新的事务,原来的挂起,新的执⾏完毕,继续执⾏⽼的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在⼀个已有的事务中执⾏,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在⼀个没有的事务中执⾏,否则抛出异常(与 Propagation.MANDATORY 相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他 bean 调⽤这个⽅法,在其他 bean 中声明事务,那就⽤事务.
如果其他 bean 没有声明事务,那就不⽤事务.
@Transactional(propagation=Propagation.NESTED)
⽀持当前事务,如果当前事务存在,则执⾏⼀个嵌套事务,如果当前没有事务,就新建⼀个事务。 -
隔离级别:可以在
@Transactional
注解中指定事务的隔离级别,如READ_UNCOMMITTED
、READ_COMMITTED
、REPEATABLE_READ
和SERIALIZABLE
。不同的隔离级别用于处理并发事务访问相同数据时可能出现的问题,如脏读、不可重复读和幻读。 -
只读事务:如果一个事务只用于读取数据而不会修改数据,可以将事务标记为只读事务,通过设置
@Transactional(readOnly = true)
来提高性能。例如,一个查询数据库统计信息的方法可以设置为只读事务。
-
3. 事务的配置
-
自动配置:Spring Boot在启动时会自动配置事务管理,只要在项目的依赖中包含了相关的数据库驱动和Spring Data JPA(或者其他支持事务管理的持久化框架),就可以直接使用事务管理功能。
-
自定义配置: Spring 事务管理器的接⼝是org.springframework.transaction.PlatformTransactionManager,通过这个接⼝,Spring 为各个平台如 JDBC、Hibernate 等都提供了对应的事务管理器,但是具体的实现就是各个平
台⾃⼰的事情了。此接⼝的内容如下:public interface PlatformTransactionManager(){ // 由 TransactionDefinition 得到 TransactionStatus 对象 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交 void commit(TransactionStatus status) throws TransactionException; // 回滚 void rollback(TransactionStatus status) throws TransactionException; }
如果需要自定义事务管理器,可以通过创建一个
DataSourceTransactionManager
(用于关系型数据库)或者其他类型的事务管理器(如JtaTransactionManager
用于分布式事务),并将其配置为一个Bean。例如:import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jdbc.DataSourceTransactionManager; @Configuration @EnableTransactionManagement public class TransactionConfig {@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);} }
这个配置类创建了一个
DataSourceTransactionManager
作为事务管理器,它需要一个DataSource
作为参数,用于管理基于该数据源的事务。 -
配置事务相关通知:⼀般来说增删改⽅法 propagation=Required,对于查询⽅法使⽤ read-only=“true”
<!- tx:method的属性: name 是必须的,表示与事务属性关联的⽅法名(业务⽅法名),对切⼊点进⾏细化。
通配符()可以⽤来指定⼀批关联到相同的事务属性的⽅法。 如:'get’、‘handle*’、'on*Event’等等.
propagation 不是必须的,默认值是REQUIRED 表示事务传播⾏为, 包括:
REQUIRED,SUPPORTS,MANDATORY,NEVER REQUIRES_NEW,NOT_SUPPORTED,NESTED
isolation 不是必须的,默认值DEFAULT 表示事务隔离级别(数据库的隔离级别) timeout
不是必须的,默认值-1(永不超时) 表示事务超时的时间(以秒为单位) read-only 不是必须的,默认值false不是只读的
表示事务是否只读 rollback-for 不是必须的 表示将被触发进⾏回滚的 Exception(s);以逗号分开。
如:‘com.foo.MyBusinessException,ServletException’ no-rollback-for
不是必须的 表示不被触发进⾏回滚的 Exception(s);以逗号分开。
如:‘com.foo.MyBusinessException,ServletException’ 任何 RuntimeException
将触发事务回滚–><tx:advice id="txAdvice" transaction-manager="txManager"><!--对以add update delete query开头的所有⽅法进⾏事务处理--><tx:attributes><!--定义什么⽅法需要使⽤事务 name代表的是⽅法名(或⽅法匹配)--><!-- 匹配以 add 开头的所有⽅法均加⼊事务 --><tx:method name="add*" propagation="REQUIRED" /><!-- 匹配以 update 开头的所有⽅法均加⼊事务 --><tx:method name="update*" propagation="REQUIRED" /><!-- 匹配以 delete 开头的所有⽅法均加⼊事务 --><tx:method name="delete*" propagation="REQUIRED" /><!-- 匹配以 query 开头的所有⽅法均加⼊事务 --><tx:method name="query*" read-only="true" /></tx:attributes></tx:advice>