Spring的事务管理详解
1、自定义的事务管理器
1.1 编程式事务
- 通过在业务层中注入事务管理器对象。然后通过编码的方式进行事务的控制
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="studentService" class="com.liu.sql.service.StudentServiceImpl">
<property name="studentMapper" ref="studentMapper"/>
<property name="platformTransactionManager" ref="transactionManager"/>
</bean>
private PlatformTransactionManager platformTransactionManager;
public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
this.platformTransactionManager = platformTransactionManager;
}
@Override
public void save(Student student) {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus status = platformTransactionManager.getTransaction(transactionDefinition);
try {
studentMapper.save(student);
platformTransactionManager.commit(status);
} catch (TransactionException e) {
e.printStackTrace();
platformTransactionManager.rollback(status);
} finally {
System.out.println("执行完毕!");
}
}
1.2 声明式事务
- 通过利用aop切面编程进行事务控制,并对事务属性在配置文件中完成细粒度的配置(精确到增删改查的每个详细部分),这种方式称为声明式事务
- 优势: 通用,减少代码冗余,更加关注于业务层的逻辑开发,无需重复进行编码。
public class TransactionAdvice implements MethodInterceptor {
private PlatformTransactionManager platformTransactionManager;
public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
this.platformTransactionManager = platformTransactionManager;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus status = platformTransactionManager.getTransaction(transactionDefinition);
try {
Object result = methodInvocation.proceed();
platformTransactionManager.commit(status);
return result;
} catch (Exception exception) {
platformTransactionManager.rollback(status);
exception.printStackTrace();
}
return null;
}
}
<bean id="studentService" class="com.liu.sql.service.StudentServiceImpl">
<property name="studentMapper" ref="studentMapper"/>
</bean>
<bean id="transactionAdvice" class="com.liu.sql.advice.TransactionAdvice">
<property name="platformTransactionManager" ref="transactionManager"/>
</bean>
<aop:config>
<aop:pointcut id="pc" expression="within(com.liu.sql.service.*ServiceImpl)"/>
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
</aop:config>
@Override
public void save(Student student) {
studentMapper.save(student);
int a = 1 / 0;
System.out.println("执行完毕!");
}
2. Spring框架开发声明式事务编程
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
a) spring框架提供的是 <tx:advice> 标签
作用:
1)可以指定的事务管理器在工厂中创建一个基于事务的环绕通知对象
<tx:advice id="注册在spring容器中的id(可以自定义名字)" transaction-manager="事务管理器对象id"/>
id:基于事务管理器对象创建的环绕通知对象在工厂中的唯一标识
2)<tx:advice>标签可以对事务进行细粒度的控制
注意:该<tx:advice>标签直接等价于前面子自定义的声明式事务的实现,也就是等价于下面的代码部分
public class TransactionAdvice implements MethodInterceptor {
private PlatformTransactionManager platformTransactionManager;
public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
this.platformTransactionManager = platformTransactionManager;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus status = platformTransactionManager.getTransaction(transactionDefinition);
try {
Object result = methodInvocation.proceed();
platformTransactionManager.commit(status);
return result;
} catch (Exception exception) {
platformTransactionManager.rollback(status);
exception.printStackTrace();
}
return null;
}
}
<bean id="transactionAdvice" class="com.liu.sql.advice.TransactionAdvice">
<property name="platformTransactionManager" ref="transactionManager"/>
</bean>
事务传播:值在多个业务层之间互相调用时传递事务的过程称之为事务传播
将事务对象在业务层之间进行传递的过程
propagation:事务传播属性的设置
REQUIRED:需要事务 如果外部没有事务 则开启新的事务 如果外部存在事务,则融入当前事务
SUPPORTS:支持事务(一般用于查询) 如果外部没有事务 不会开启新的事务 如果外部存在事务,则融入当前事务
REQUIRES_NEW:每次都开始新的事务 如果外部存在事务,外层事务进行挂起,自己开启新的事务执行。执行完成,恢复外层事务继续执行
NOT_SUPPORTED:不支持事务 如果外部存在事务,外层事务进行挂起,自己以非事务执行。执行完成,恢复外层事务继续执行
NEVER:不能有事务 存在事务就会出错
MANDATORY:强制事务 不存在事务就会报错
NESTED:嵌套事务 事物之间可以嵌套运行
isolation:事务的隔离级别
DEFAULT: 使用数据库默认的隔离级别(推荐)
READ_UNCOMMITTED: 读未提交 一个客户端能读取到另一个客户端没有提交的数据 脏读现象
READ_COMMITTED: 读已提交 一个客户端只能读取到已经提交的数据 避免脏读现象
REPEATABLE_READ: 可重复读 主要用来避免不可重复读的现象出现 行锁 mysql
SERIALIZABLE: 序列化读 主要用来避免幻读现象出现 表锁
注意:隔离级别越高,查询效率越低,一般推荐使用默认的数据库隔离级别
b)具体的配置使用
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" isolation="REPEATABLE_READ"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="within(com.liu.sql.service.*ServiceImpl)"/>
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
</aop:config>
@Transactional:
- 作用:用来给类中的方加入事务控制,简化配置文件,该注解等价于上面的两个标签: <tx:advice> + <aop:config> 两个部分相加,所以以后写上该注解,就不要再写上面的配置文件了
- 修饰范围:类 方法 局部优先原则
1. 加在类上, 代表类中的所有方法加入事务控制
2. 加在方法上。代表当前方法加入事务控制
3. 类和方法上同时存在 方法优先
使用的要求:
- 如果要让@Transactional这个注解生效,在配置文件中需要加入如下配置
- 查询方法显示的配置事务的传播属性为 SPUPPORT 方式
- 数据源
<!-- 数据源事务管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源对象 -->
<property name="dataSource" ref="dataSource"/>
</bean>
- <!-- 开启注解式事务生效 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
3. SM最终整合步骤 applicationContext.xml
# a、创建数据源对象
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
# b、创建sqlSessionFactory对象
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
<property name="typeAliasesPackage" value="com.liu.sql.entity"/>
</bean>
# c、创建dao对象
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.liu.sql.mapper"/>
</bean>
# d、创建事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
# e、根据事务管理器创建事务环绕通知 tx:advice
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find" propagation="SUPPORTS"/>
</tx:attributes>
# f、配置事务切面
<aop:config>
<aop:pointcut id="pc" expression="within(com.liu.sql.service.*ServiceImpl)"/>
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="pc"/>
</aop:config>