事务操作
概念
什么是事务
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败,所有操作都失败
- 典型场景:银行转账
事务四个特性(ACID):
- 原子性:不可分割
- 一致性:总量不变
- 隔离性:两个人同时操作,不会对彼此产生影响
- 持久性:提交后表中数据发生真正变化
搭建环境
-
创建数据库表,添加记录
-
创建service,搭建dao,完成对象的创建和注入。
- service注入dao
- dao注入jdbctemplate,在JDBCTemplate注入dataSource
- 在dao创建两个方法,多钱和少钱,在service创建转账方法
- 问题,在有异常时一半的方法执行
事务操作过程
try {
userDao.reduceMoney();
// 模拟异常
int i = 1 / 0;
userDao.addMoney();
//没有发生异常,事务提交
} catch (Exception e) {
//出现了异常,事务回滚
e.printStackTrace();
}
Spring事务管理介绍
- 事务一般添加在javaee三层结构里的Service层
- 在Spring进行事务管理操作有两种方式
- 编程式事务管理
- 声明式事务管理(使用)基于注解方式,基于xml配置文件方式
声明式事务管理
Spring进行声明式事务管理,底层使用AOP
Spring事务管理API
1. 提供了一个接口,代表事务管理器,这个接口针对不同的框架提供了不同的实现类
注解方式实现声明式事务管理
-
在Spring配置文件中配置事务管理器
<!-- 配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
在spring配置文件中开启事务注解
-
在spring配置文件中引入名称空间tx
<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">
-
开启事务
<!-- 开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"/>
-
-
在Service类上面(或者service类方法上面)添加事务注解
-
@Transactional
加在类和方法上均可 -
如果添加在类上面,代表类里面的所有方法都添加了事务
-
如果放在方法上面,只是为这个方法添加事务
@Service(value = "userServiceImpl") @Transactional //加在类和方法上均可 public class UserServiceImpl implements UserService { @Autowired @Qualifier("userDaoImpl") private UserDao userDao; public void accountMoney() { userDao.reduceMoney(); int i = 1 / 0; userDao.addMoney(); } }
-
声明式事务管理参数配置
-
在service类上面添加注解
@Transactional
,在这个注解里面可以配置事务相关参数 -
propagation:事务传播行为
多事务方法之间执行调用,这个过程中事务是如何进行管理的
事务方法:对数据库表进行变化的操作
七种传播行为
-
isolation:事务的隔离级别
事务有特性:隔离性,多事务操作之间不会产生影响。不考虑隔离性会产生很多问题
有三个读的问题,脏读,不可重复读,虚(幻)读
脏读:一个未提交事务读取到另一个未提交事务的数据
不可重复读:一个未提交事务读取到一个提交事务修改数据
虚读:一个未提交事务读取到另一提交事务添加数据
通过设置事务隔离级别,就能解决三个读的问题
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
-
timeout超时时间。
事务需要在一定时间内进行提交,如果不提交,就会回滚
默认值是-1,设置时间以秒(s)为单位
-
readonly:是否只读
读:查询操作,写:添加修改删除
readOnly默认为false
设置为true后,只能查询
-
rollbackfor,回滚
设置出现哪些异常进行事务回滚
-
norollbackfor:不回滚
设置出现哪些异常不进行回滚
XML声明式事务管理
-
在Spring配置文件中进行配置
第一步:配置事务管理器
第二步:配置通知
第三步:配置切入点和切面
<!-- 配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置通知--> <tx:advice id="txadvice"> <!-- 配置事务相关参数--> <tx:attributes> <!-- 指定在哪种规则的方法上添加事务--> <tx:method name="accountMoney" propagation="REQUIRED"/> <!-- <tx:method name="account*"/>--> </tx:attributes> </tx:advice> <!-- 配置切入点和切面--> <aop:config> <!-- 配置切入点--> <aop:pointcut id="pt" expression="execution(* com.yang.service.UserServiceImpl.*(..))"/> <!-- 配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
完全注解开发
-
创建配置类,使用配置类替代xml配置文件
@Configuration @ComponentScan(basePackages = "com.yang") @EnableTransactionManagement //开启事务 public class TxConfig { //创建数据库连接池 @Bean public DruidDataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=Asia/Shanghai&CharacterEncoding=utf-8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } //创建jdbcTemplate模板对象 @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { //到ioc容器中根据类型找到dataSource JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } //创建事务管理器 @Bean public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }