一、Spring事务的四大特性
-
原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不做,就像你吃一个完整的苹果,不能只吃一半就扔掉。
-
一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。比如,你从一个账户转账到另一个账户,两个账户的总金额必须保持不变。
-
隔离性(Isolation):并发执行的事务之间不会互相干扰,就像你在图书馆看书,不会影响到旁边的人。Spring提供了多种隔离级别,让你根据需要选择。
-
持久性(Durability):一旦事务提交,它对数据库的修改就是永久性的,即使系统发生故障也不会丢失。就像你在纸上写的字,不会因为你合上本子就消失。
二、spring事务管理
Spring 并不直接管理事务,⽽是提供了多种事务管理器,他们将事务管理的职责委托给 Hibernate 或 者 JTA 等持久化机制所提供的相关平台框架的事务来实现。
在Spring中,事务管理主要有两种方式:编程式事务管理和声明式事务管理。
-
编程式事务管理:通过编程的方式管理事务,这种方式比较灵活,但代码侵入性强,需要在代码中显式地调用事务管理的方法。
-
声明式事务管理:通过配置的方式管理事务,这种方式代码侵入性低,易于维护。Spring推荐使用声明式事务管理,因为它更符合Spring的“约定优于配置”的理念。
三、三大事务接口
(一)、PlatformTransactionManager
PlatformTransaction是spring事务处理的一个核心,这个接口中定义了操作事务的方法,如下:
(二)、TransactionDefinition
字面意思即事务的定义,主要是用来描述事务的规则,也成为属性,
(三)、TransactionStatus
事务的状态,查看当前事务状态
四、PlatformTransactionManager实现编程式事务
(一)、引入相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.1.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
(二)、编写配置文件
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置包扫面-->
<context:component-scan base-package="com.wedu"/>
<!-- 引入连接数据库的文件-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!-- 配置数据源直接使用spring提供的-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 提供事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!-- 通过模板类操作操作事务 -->
<bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<!-- 通过JdbcTemplate操作数据库-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="datasource"></property>
</bean>
</beans>
在数据库创建·counts表,并且添加数据
1、通过TransactionManager实现
@Service
public class CountsService {
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Autowired
private JdbcTemplate jdbcTemplate;
//模拟转账操作
public void transfer(){
//开启一个事务,这里采用默认事务规则
final TransactionStatus status = platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
try {
jdbcTemplate.update("update counts set money=? where id=?",66,2);
platformTransactionManager.commit(status);
} catch (DataAccessException e) {
platformTransactionManager.rollback(status);
throw new RuntimeException(e);
}
}
}
public class CountsTest {
public static void main(String[] args) {
final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
final CountsService countService = (CountsService) context.getBean("countsService");
countService.transfer();
}
}
2、通过TransactionTemplate实现
@Service
public class CountsService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
//模拟转账操作
public void transfer(){
// //开启一个事务
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
jdbcTemplate.update("update counts set money=? where id=?",99,1);
} catch (Exception e) {
System.out.println("转账失败");
//进行事务回滚
status.setRollbackOnly();
}
}
});
}
}
测试
public class CountsTest {
public static void main(String[] args) {
final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
final CountsService countService = (CountsService) context.getBean("countsService");
countService.transfer();
}
}
五、声明式事务
(一)、xml配置实现声明式事务
编写配置文件
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置包扫面-->
<context:component-scan base-package="com.wedu"/>
<!-- 引入连接数据库的文件-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!-- 配置数据源直接使用spring提供的-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="datasource"></property>
</bean>
<!--
xml配置事务分为三个步骤
1、配置事务管理器
2、配置事务通知
3、配置aoop
-->
<!--配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!-- 配置事务通知-->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer*"/>
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<aop:pointcut id="pointCut" expression="execution(* com.wedu.service.*.*(..))"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="pointCut"></aop:advisor>
</aop:config>
</beans>
引入依赖
测试
@Service
public class CountsService {
@Autowired
private JdbcTemplate jdbcTemplate;
//模拟转账操作
public void transfer() {
jdbcTemplate.update("update counts set money=? where id=?", 88, 2);
}
}
public class CountsTest {
public static void main(String[] args) {
final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
final CountsService countService = (CountsService) context.getBean("countsService");
countService.transfer();
}
}
(二)、Java配置声明式事务
编写配置类
@Configuration
//配置包扫描
@ComponentScan(basePackages = "com.wedu")
//开启事务的注解支持
@EnableTransactionManagement
public class JavaConfig {
//创建数据源
@Bean
DataSource dataSource(){
DriverManagerDataSource dataSource=new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/wedustudy?serverTimezone=GMT%2B8&" +
"useUnicode=true&useSSL=false&characterEncoding=utf8");
dataSource.setUrl("root");
dataSource.setPassword("root");
return dataSource;
}
//创建JdbcTemplate
@Bean
JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
//创建事务管理器
PlatformTransactionManager platformTransactionManager(){
return new DataSourceTransactionManager(dataSource());
}
}
测试
@Service
public class CountsService {
@Autowired
private JdbcTemplate jdbcTemplate;
//模拟转账操作
//开启事务
@Transactional
public void transfer() {
jdbcTemplate.update("update counts set money=? where id=?", 9, 2);
final int i = 1 / 0;
}
}
public class CountsTest2 {
public static void main(String[] args) {
final ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
final CountsService countService = (CountsService) context.getBean("countsService");
countService.transfer();
}
}
(三)、xml和java混合配置
在xml配置文件中加入事务注解支持,然后在Java代码方法或者类上添加@transactional注解即可
<tx:annotation-driven/>
六、事务的传播行为
1、required
如果当前存在事务,则加入当前事务,如果当前不存在事务,则新建一个事务。
2、requires_new 创建一个新的事务,如果当前存在事务,则把当前事务挂起
3、nested 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行
4、mandatory如果存在事务则加入当前事务,如果不存在则抛出异常
5、supports 如果当前存在事务则加入当前事务,不存在则非事务方式运行
6、not_supported 以非事务运行,如果当前存在事务则挂起当前事务
7、never:以非事务的方式运行,有事务则抛出异常
七、声明式事务和编程是事务区别
编程式事务就像是那些喜欢自己掌控一切的运动员,他们喜欢亲自下场,精细地控制每一个动作。在编程中,这意味着开发者需要手动编写代码来管理事务的边界,包括何时开始事务、何时提交事务以及何时回滚事务。这种方式给了开发者很高的灵活性和控制力,但同时也增加了代码的复杂度和出错的可能性。
声明式事务则更像是那些擅长制定策略、让团队去执行的教练。在Spring等框架中,开发者不需要在代码中显式地管理事务,而是通过在配置文件中声明事务规则,或者通过注解等方式来指定哪些方法需要事务支持。框架会自动为这些方法创建事务,并在方法执行完毕后根据执行结果提交或回滚事务。这种方式简化了代码,降低了出错的可能性,同时也使得业务逻辑更加清晰。
简单来说,编程式事务和声明式事务的主要区别在于:编程式事务需要开发者手动控制事务的边界,而声明式事务则通过配置或注解的方式自动管理事务。两者各有优劣,选择哪种方式取决于具体的应用场景和团队的技术栈。