7.1 事务管理方案
在Spring框架中提供了两种事务管理方案:
- 编程式事务:通过编写代码实现事务管理。
- 声明式事务:基于AOP技术实现事务管理。
在Spring框架中,编程式事务管理很少使用,我们对声明式事务管理进行详细学习。
Spring的声明式事务管理在底层采用了AOP技术,其最大的优点在于无需通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。
使用AOP技术为service方法添加如下通知:
7.2 事务管理器
Spring依赖事务管理器进行事务管理,事务管理器即一个通知类,我们为该通知类设置切点为service层方法即可完成事务自动管理。由于不同技术操作数据库,进行事务操作的方法不同。如:JDBC提交事务是connection.commit()
,MyBatis提交事务是sqlSession.commit()
,所以Spring提供了多个事务管理器。
事务管理器名称 | 作用 |
org.springframework.jdbc.datasource.DataSourceTransactionManager | 针对JDBC技术提供的事务管理器。适用于JDBC和MyBatis。 |
org.springframework.orm.hibernate3.HibernateTransactionManager | 针对于Hibernate框架提供的事务管理器。适用于Hibernate框架。 |
org.springframework.orm.jpa.JpaTransactionManager | 针对于JPA技术提供的事务管理器。适用于JPA技术。 |
org.springframework.transaction.jta.JtaTransactionManager | 跨越了多个事务管理源。适用在两个或者是多个不同的数据源中实现事务控制。 |
我们使用MyBatis操作数据库,接下来使用DataSourceTransactionManager
进行事务管理。
1、引入依赖
<!-- 事务管理 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.13</version> </dependency> <!-- 事务底层 AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
2、配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--扫描包--> <context:component-scan base-package="com.zj"/> <!--读取数据源配置文件--> <context:property-placeholder location="classpath:db.properties"/> <!--配置数据源--> <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--创建Spring封装过的SqlSessionFactory对象--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource"/> </bean> <!--创建Spring封装的SqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <!-- 该对象可以自动扫描持久层接口,并为接口创建代理对象 --> <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 配置扫描的接口包 --> <property name="basePackage" value="com.zj.dao"/> </bean> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"/> </bean> <!--事务相关配置--> <tx:advice id="txAdvice"> <!--暂时都用默认配置--> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> <!--配置切面(切点+通知)--> <aop:config> <!--切点--> <aop:pointcut id="point" expression="execution(* com.zj.service.*(..))"/> <!--通知--> <aop:advisor advice-ref="txAdvice" pointcut-ref="point"/> </aop:config> </beans>
3、再次测试
账户 没变说明事务管理是成功的。
7.3 事务控制的API
Spring进行事务控制的功能是由三个接口提供的,这三个接口是Spring实现的,在开发中我们很少使用到,只需要了解他们的作用即可:
PlatformTransactionManager接口
PlatformTransactionManager是Spring提供的事务管理器接口,所有事务管理器都实现了该接口。该接口中提供了三个事务操作方法:
- TransactionStatus getTransaction(TransactionDefinition definition):获取事务状态信息。
- void commit(TransactionStatus status):事务提交
- void rollback(TransactionStatus status):事务回滚
TransactionDefinition接口
TransactionDefinition是事务的定义信息对象,它有如下方法:
- String getName():获取事务对象名称。
- int getIsolationLevel():获取事务的隔离级别。
- int getPropagationBehavior():获取事务的传播行为。
- int getTimeout():获取事务的超时时间。
- boolean isReadOnly():获取事务是否只读。
TransactionStatus接口
TransactionStatus是事务的状态接口,它描述了某一时间点上事务的状态信息。它有如下方法:
- void flush() 刷新事务
- boolean hasSavepoint() 获取是否存在保存点
- boolean isCompleted() 获取事务是否完成
- boolean isNewTransaction() 获取是否是新事务
- boolean isRollbackOnly() 获取是否回滚
- void setRollbackOnly() 设置事务回滚
7.4 事务的相关配置
在<tx:advice>
中可以进行事务的相关配置:
<tx:advice id="txAdvice"> <tx:attributes> <tx:method name="*"/> <tx:method name="find*" read-only="true"/> </tx:attributes> </tx:advice>
<tx:method>
中的属性:
- name:指定配置的方法。
*
表示所有方法,find*
表示所有以find开头的方法。- read-only:是否是只读事务,只读事务不存在数据的修改,数据库将会为只读事务提供一些优化手段,会对性能有一定提升,建议在查询中开启只读事务。
- timeout:指定超时时间,在限定的时间内不能完成所有操作就会抛异常。默认永不超时
- rollback-for:指定某个异常事务回滚,其他异常不回滚。默认所有异常回滚。
- no-rollback-for:指定某个异常不回滚,其他异常回滚。默认所有异常回滚。
- propagation:事务的传播行为
- isolation:事务的隔离级别
7.5 事务的传播行为
事务传播行为是指多个含有事务的方法相互调用时,事务如何在这些方法间传播。
如果在service层的方法中调用了其他的service方法,假设每次执行service方法都要开启事务,此时就无法保证外层方法和内层方法处于同一个事务当中。
// method1的所有方法在同一个事务中 public void method1(){ // 此时会开启一个新事务,这就无法保证method1()中所有的代码是在同一个事务中 method2(); System.out.println("method1"); } public void method2(){ System.out.println("method2"); }
事务的传播特性就是解决这个问题的,Spring帮助我们将外层方法和内层方法放入同一事务中。
传播行为 | 介绍 |
REQUIRED | 默认。支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 必须在事务状态下执行,如果没有事务则新建事务,如果当前有事务则创建一个嵌套事务 |
7.6 事务的隔离级别
事务隔离级别反映事务提交并发访问时的处理态度,隔离级别越高,数据出问题的可能性越低,但效率也会越低。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
READ_UNCOMMITTED(读取未提交内容) | Yes | Yes | Yes |
READ_COMMITTED(读取提交内容) | No | Yes | Yes |
REPEATABLE_READ(重复读) | No | No | Yes |
SERIALIZABLE(可串行化) | No | No | No |
如果设置为DEFAULT会使用数据库的隔离级别。
- SqlServer , Oracle默认的事务隔离级别是READ_COMMITTED
- Mysql的默认隔离级别是REPEATABLE_READ
7.7 注解配置声明式事务
Spring支持使用注解配置声明式事务。用法如下:
1、注册事务注解驱动
<!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"/> </bean> <!--注册事务注解驱动--> <tx:annotation-driven transaction-manager="transactionManager"/>
2、在需要事务支持的方法或类上加@Transactional
@Service @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT) public class UserService {}
配置类代替xml中的注解事务支持:在配置类上方写@EnableTranscationManagement
@Configuration @ComponentScan("com.zj") @EnableTransactionManagement public class SpringConfig { @Bean public DataSource getDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); druidDataSource.setUrl("jdbc:mysql:///mybatis"); druidDataSource.setUsername("root"); druidDataSource.setPassword("123456"); return druidDataSource; } @Bean public SqlSessionFactoryBean getSqlSession(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } @Bean public MapperScannerConfigurer getMapperScanner(){ MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage("com.zj.dao"); return mapperScannerConfigurer; } @Bean public DataSourceTransactionManager getTransactionManager(DataSource dataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }