目录
一、事务概述
二、引入事务场景
三、Spring对事务的支持
Spring实现事务的两种方式
声明式事务之注解实现方式
1.在Spring配置文件中配置事务管理器
2. 在Spring配置文件引入tx命名空间
3. 在Spring配置文件中配置“事务注解驱动器”,通过注解的方式控制事务
4. Service类或方法上添加@Transaction注解
5.执行转账操作
声明式事务之全注解开发
1.配置类替代配置文件
2.测试程序
3.测试结果
声明式事务之XML实现方式
1.Spring配置文件
2.编写测试程序
3.测试结果
一、事务概述
- 在一个业务流程中,通常需要多条DML语言共同联合完成,这多条DML语句必须同时成功或失败,这样才能保证数据的安全。
- 事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。例如:在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。
- 事务的四个处理流程
- 开启事务(begin transaction)
- 执行核心业务代码
- 提交事务(业务执行过程没有出现异常) (commit transaction)
- 回滚事务(业务执行过程中出现异常)(rollback transaction)
- 事务的ACID特性
- 原子性(Atomicity)事务小的工作单元,不可划分。
- 一致性(Consistency)事务要么同时成功,要么同时失败。
- 隔离性(Isolation)隔离性实际上解决的问题是多个事务作用于同一个或者同一批数据时的并发问题,防止由于交叉执行而导致数据的不一致。一个完美的事务隔离,在每个事务看来,整个系统只有自己在工作,对于整个系统而言这些并发的事务一个接一个的执行,也仿佛只有一个事务,这样的隔离成为“可序列化(Serializability)”。当然,这样的隔离级别会带来巨大的开销,因此出现了各种各样的隔离级别,进而满足不同场景的需要。
- 持久性(Durability)只要事务完成,不管发生任何问题,都不应该发生数据丢失。即,事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
二、引入事务场景
以银行转账为例 ,有两个账户act1 和 act2,act1向act2转账10000,对于这个流程包括两个操作,act1减10000,act2加10000,这两步必须同时成功或者同时失败。
- 转账成功,act 减10000, act2 加 10000
- 转账失败,act减10000后,因系统故障,act2没有加10000
数据库为了避免这种特殊情况的发生,提出了事务概念。例如:通过jdbc连接数据库后,可以手动开启事务,提交事务,回滚事务。我们可以借助aop技术实现事务管理,更幸运的是Spring提供了事务支持。
三、Spring对事务的支持
-
Spring实现事务的两种方式
- 编程式事务 通过编写代码的方式实现事务的管理
- 声明式事务 基于注解方式、XML配置方式
-
声明式事务之注解实现方式
1.在Spring配置文件中配置事务管理器
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dateSource"/>
</bean>
2. 在Spring配置文件引入tx命名空间
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://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">
3. 在Spring配置文件中配置“事务注解驱动器”,通过注解的方式控制事务
<!--开启事务注解驱动器,开始事务注解。-->
<tx:annotation-driven transaction-manager="txManager"/>
4. Service类或方法上添加@Transaction注解
package com.xxx.bank.service.impl;
import com.xxx.bank.dao.AccountDao;
import com.xxx.bank.pojo.Account;
import com.xxx.bank.service.AccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
@Resource(name = "accountDao")
private AccountDao accountDao;
@Override
public void transfer(String fromActno, String toActno, double money) {
Account fromAct = accountDao.selectByActno(fromActno);
if (fromAct.getBalance() < money) {
throw new RuntimeException("余额不足,无法转账!!!");
}
Account toAct = accountDao.selectByActno(toActno);
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
int count = accountDao.update(fromAct);
count += accountDao.update(toAct);
/*
模拟异常
String s = null;
s.toString();
*/
if (count != 2) {
throw new RuntimeException("转账失败,请联系银行...");
}
}
}
5.执行转账操作
当前账户情况
执行一次转账
模拟异常情况,检查是否转账成功
-
声明式事务之全注解开发
1.配置类替代配置文件
package com.zhj.bank;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* @author zhj1259615665@qq.com
* @create 2023-10-04 {16:45}
*/
@Configuration
// 开启组件扫描
@ComponentScan("com.zhj.bank")
// 开启事务管理
@EnableTransactionManagement
public class Spring6Config {
@Bean(name = "dataSource")
public DruidDataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3307/spring");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean(name = "txManager")
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager txManager = new DataSourceTransactionManager();
txManager.setDataSource(dataSource);
return txManager;
}
}
2.测试程序
@Test
public void testNoXml() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
try {
accountService.transfer("act-001", "act-002", 10000);
System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
try {
accountService.transfer("act-001", "act-002", 10000);
System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
}
}
3.测试结果
-
声明式事务之XML实现方式
1.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: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 http://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.zhj.bank"/>
<!--配置数据源-->
<bean id="dateSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3307/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dateSource"/>
</bean>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dateSource"/>
</bean>
<!--配置通知,具体的增强代码-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.zhj.bank.service..*(..))"/>
<!--切面 = 切点 + 通知-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
2.编写测试程序
public class BankTxTest {
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
try {
accountService.transfer("act-001", "act-002", 10000);
System.out.println("转账成功...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.测试结果
转账一次
模拟异常情况
事务在数据库中是十分重要的概念,Spring提供了两种事务的支持方式,这篇博客记录了一点点的知识点,通过银行转账案例希望可以帮助你更好地理解事务。上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!