1.事务管理
-
概念:
事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,要么都失败,
如果有一个失败操作那么所有的操作都失败
-
事务四个特性(ACID)
-
原子性(Atomicity):
指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
-
一致性(Consistency):
事务前后数据的完整性必须保持一致。
-
隔离性(Isolation):
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
-
持久性(Durability):
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
事务的运行
- 开启事务
- 进行业务操作
- 没有发生异常,提交事务
- 发生异常,进行事务回滚
2.声明式事务管理
2.1 实现步骤
-
创建配置事务管理器
DataSourceTransactionManage (针对JdbcTemplate或Mybatis)
-
开启事务
@EnableTransactionManagement 或者配置 < tx:annotation-driven/>
-
在需要进行事务处理的类或者方法上添加事务注解
@Transactional
(1)基于注解实现
配置类:SpringConfig.java
@Configuration
@ComponentScan(basePackages = {"com.potato"})
@EnableTransactionManagement // 2.开启事务注解
public class SpringConfig {
//1.创建配置事务管理器 DataSourceTransactionManager
@Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "dataSource")
public DataSource createDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring5");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate createDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
}
需要事务处理的类:BankServiceImpl.java
//3.在需要进行事务处理的类或者方法上添加事务注解
@Transactional
@Override
public Integer transferAccounts(String saveAccount, String takeAccount, int money) {
Integer take = bankDao.take(takeAccount, money);
Integer save = bankDao.save(saveAccount, money);
return take == save ? (take != 0 ? 1 : 0) : 0;
}
(2)基于xml配置文件实现
配置文件
<context:component-scan base-package="com.potato"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.driver.jdbc.mysql"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/spring5"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
<!-- 1.创建事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2.开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
2.2 Spring事务管理API :PlatformTransactionManager接口
PlatformTransactionManager:事务管理的接口,有多个不同的实现类分别针对不同的数据处理框架
DataSourceTransactionManager :JdbcTemplate、Mybatis
HibernateTransactionManager:Hibernate
2.3 @Transactional 相关参数
propagation:事务传播行为
isolation:事务隔离级别
timeout:超时时间
readOnly:是否只读
rollbackFor:发生指定的异常则回滚
noRollbackFor:发生指定的异常则不回滚
2.4 传播行为:propagation
传播属性 | 解析 |
---|---|
REQUIRED(默认) | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中, 就继续使用该事务。这是最常见的选择。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务, 则执行与REQUIRED类似的操作。 |
2.5 隔离级别:isolation
-
不考虑隔离性 则会产生以下三个问题
-
脏读:
在多事务之间:一个未提交的事务读取到另一个未提交事务的数据
-
不可重复读
两个事务,事务A与事务B,事务A在自己执行的过程中,执行了两次相同查询,
第一次查询事务时B未提交,第二次查询事务时B已提交,从而造成两次查询结果不一样
-
幻读(虚读)
在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
-
-
隔离级别参数
属性值 脏读 不可重复读 幻读 READ_UNCOMMITTED(读未提交) 有 有 有 READ_COMMITTED(读以提交) 无 有 有 REPEATABLE_READ(可重复读) (默认) 无 无 有 SERIALIZABLE(串行化) 无 无 无
2.6 其他参数
-
timeout:超时时间 秒为单位
事务在一定时间内进行提交,如果不提交则进行回滚
-
readOnly:是否只读
(1)1.读:查询操作 2.写:增删改操作
(2)默认值为 false, 表示可以增删改查
(3)设置为true 则是只读操作
-
rollbackFor:回滚
(1)设置出现哪些异常需要进行事务回滚
-
noRollbackFor:不回滚
(1)设置出现哪些异常不需要进行事务回滚
3.编程式事务管理
-
实现步骤
1.配置事务管理器
2.配置通知
3.配置切入点和切面
spring-config.xml
<context:property-placeholder location="druid.properties"/> <context:component-scan base-package="com.potato"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource"/> </bean> <!-- 1 创建事务管理器--> <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 2 配置通知--> <tx:advice id="txAdvice"> <!-- 2.1 配置事务参数--> <tx:attributes> <!-- 2.2 指定哪种方法上添加事务 名字以transfer开头的方法添加事务--> <tx:method name="transfer*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 3 配置切面--> <aop:config> <!-- 3.1 配置切入点--> <aop:pointcut id="pointcut01" expression="execution(* com.potato.service.BankService.*(..))"/> <!-- 3.2 配置切面--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut01"/> </aop:config>
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/289035.html