目录
事务传播机制:就是事务在多个方法的调用中是如何传递的,是重新创建事务还是使用父方法的事务?父方法的回滚对子方法的事务是否有影响?这些都是可以通过事务传播机制来决定的。
以spring的事务传播机制为例子:
Spring事务机制:主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考。
Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。再也无需要我们在与事务相关的方法中处理大量的try…catch…finally代码。我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。
嵌套事务: 一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
1、事务的传播机制
事务的传播机制定义在TransactionDefinition接口中,我们也可以通过枚举类Propagation类调用,下面我也附上两个类的源码:
- Propagation.REQUIRED:支持当前事务,如果当前没有事务,则新建一个事务,默认使用这种,也是最常见的 。
- Propagation.SUPPORTS:支持当前事务,如果没有事务,就以非事务(即数据库事物)的方式执行。
- Propagation.MANDATORY:支持当前事务,如果没有事务,就抛出异常.
- Propagation.REQUIRES_NEW:新建事务,如果当前存在事务,就把当前事务挂起。
- Propagation.NOT_SUPPORTED:以非事务的方式执行操作,如果当前存在事务,就把当前事务挂起.
- Propagation.NEVER:以非事务的方式执行,如果当前存在事务,则会抛出异常.
- Propagation.NESTED:如果当前事务存在,则执行嵌套事务,否则执行类似REQUIRED的操作.
2、测试
2.1、准备测试方法
- 开启事务 配置
<?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:aop="http://www.springframework.org/schema/aop"
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/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">
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
- 创建实体类User
package com.example.demo.testspringtransaction;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author
* @since 2021年1月13日 下午3:36:34
* @version 1.0
*
* @Data :注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法
* @Setter:注解在属性上;为属性提供 setting 方法
* @Getter:注解在属性上;为属性提供 getting 方法
* @Log4j :注解在类上;为类提供一个 属性名为log 的 log4j 日志对象
* @NoArgsConstructor:注解在类上;为类提供一个无参的构造方法
* @AllArgsConstructor:注解在类上;为类提供一个全参的构造方法
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String id;
private String name;
}
- 创建service接口UserService1 、UserService2
package com.example.demo.testspringtransaction;
/**
* @author
* @since 2021年1月13日 下午3:48:27
* @version 1.0
*
*/
public interface UserService1 {
public void save(User user);
public void update(User user);
}
package com.example.demo.testspringtransaction;
/**
* @author
* @since 2021年1月13日 下午3:48:27
* @version 1.0
*
*/
public interface UserService2 {
public void delete(String id);
}
- 创建service实现类:UserService1Impl 、UserService2Impl
package com.example.demo.testspringtransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @author
* @since 2021年1月13日 下午3:56:02
* @version 1.0
*
*/
@Service
@Transactional(propagation=Propagation.REQUIRED)
public class UserService1Impl implements UserService1 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService2 userService2;
@Override
public void save(User user) {
String sql = "insert into user values(?,?)";
jdbcTemplate.update(sql,
new Object[]{user.getId(),user.getName()},
new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR});
userService2.delete("002");
// update(user);
// throw new RuntimeException("error");
}
@Override
public void update(User user) {
String sql = "update user set name = ? where id=?";
jdbcTemplate.update(sql, new Object[]{user.getName(),user.getId()},
new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR});
}
}
package com.example.demo.testspringtransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* @author
* @since 2021年1月13日 下午3:56:02
* @version 1.0
*
*/
@Service
@Transactional(propagation=Propagation.REQUIRED)
public class UserService2Impl implements UserService2 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void delete(String id) {
String sql = "delete from user where id=?";
jdbcTemplate.update(sql, id);
}
}
- 创建Configuration类,用于创建DataSource实现
package com.example.demo.testspringtransaction;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
@Configuration
@ComponentScan(basePackages={"com.example.demo.testspringtransaction"})// 扫描UserService1实现类所在的包路径
@ImportResource(locations={"classpath:beans.xml"})// 添加事务管理
public class JdbcConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean
public DataSource dataSource(){
try {
return new SimpleDriverDataSource(new com.mysql.jdbc.Driver(), "jdbc:mysql://localhost:3306/springboot", "root", "123456");
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
- 测试类
package com.example.demo.testspringtransaction;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author
* @since 2021年1月13日 下午4:07:42
* @version 1.0
*
*/
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(JdbcConfig.class);
UserService1 userService1 = ac.getBean(UserService1.class);
User user = new User("001","唐僧");
userService1.save(user);
}
}
2.2、事务传播机制的测试
2.2.1、REQUIRED
定义:如果有事务则加入事务,如果没有事务,则创建一个新的(默认值)。
注意事项:当两个方法的传播机制都是REQUIRED时,如果一旦发生回滚,两个方法都会回滚。
测试点1:将UserService1Impl和BlogService2Impl的事务传播机制都修改为@Transactional(propagation=Propagation.REQUIRED)
测试点2:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),UserService2Impl的仍为@Transactional(propagation=Propagation.REQUIRED)
总结:
当UserService1Impl提供事务的时候,UserService2Impl的方法执行使用当前已有事务,不再新建事务;
当UserService1Impl不创建事务的时候,UserService2Impl的方法执行发现没有事务可用,自己新建事务;
2.2.2、NOT_SUPPORTED
定义:不为当前方法开启事务,相当于没有Spring事务,每条执行语句单独执行,单独提交。
测试点3:将UserService1Impl和BlogService2Impl的事务传播机制都修改为:@Transactional(propagation=Propagation.NOT_SUPPORTED)
总结:
NOT_SUPPORTED相当于没有Spring事务,每条执行语句单独执行,单独提交。
2.2.3、REQUIRES_NEW
定义:不管是否存在事务,都创建一个新的事务,原来的方法挂起,新的方法执行完毕后,继续执行老的事务。 老事务报错,老失误回滚,并不影响新事务的提交。
测试点4:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogService2Impl的改为@Transactional(propagation=Propagation.REQUIRES_NEW)
总结:
REQUIRES_NEW为当前方法创建一个新的事务,并且当前事务先提交,然后再提交老的事务。
2.2.4、MANDATORY
定义:必须在一个已有的事务中执行,否则报错。报错时,前一个方法中的非事务处理,不回滚。
测试点5:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),UserService2Impl的改为@Transactional(propagation=Propagation.MANDATORY),查看是否报错。
总结:
MANDATORY必须在已有事务下被调用,否则报错。
NOT_SUPPORTED执行数据库层面的事务操作,故当前测试中,insert方法成功执行,delete方法的抛错并不影响insert方法的执行。
2.2.5、NEVER
定义:必须在一个没有的事务中执行,否则报错。
测试点6:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),UserService2Impl的仍为@Transactional(propagation=Propagation.NEVER),查看是否报错。
总结:
NEVER必须在没有事务的方法中执行,否则报错;
save方法开启一个事务,还没来及提交发现delete方法报错,只能回滚事务。
2.2.6、SUPPORTS
定义:是否使用事务取决于调用方法是否有事务,如果有则直接用,如果没有则不使用事务。
测试点7:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),UserService2Impl的仍为@Transactional(propagation=Propagation.SUPPORTS)
测试点8:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),UserService2Impl的仍为@Transactional(propagation=Propagation.SUPPORTS)
总结:
SUPPORTS类型的事务传播机制,是否使用事务取决于调用方法是否有事务,如果有则直接用,如果没有则不使用事务。
2.2.7、NESTED
定义:如果当前存在事务,则在嵌套事务内执行(当前事务回滚,嵌套事务也回滚;嵌套事务回滚,不影响当前事务)。如果当前没有事务,则执行与REQUIRED类似的操作。
测试点9:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),UserService2Impl 改为@Transactional(propagation=Propagation.NESTED)
测试点10:将UserService1Impl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),UserService2Impl 改为@Transactional(propagation=Propagation.NESTED)
总结:
save方法创建一个事务,则再调用delete方法时,直接在该事务的基础上创建一个嵌套事务,本质上还是同一个事务,做一次提交;
save方法不创建事务,则调用delete方法时,直接创建一个新的事务,单独提交。
3、spring事务的五种隔离级别
目录
ISOLATION_DEFAULT:是事务管理器的默认隔离级别,使用数据库默认的隔离级别。另外四个与jdbc的隔离级别 相对应
ISOLATION_READ_UNCOMMITTED:最低的隔离级别,它允许一个事务读取另一个事务未提交的数据,会产生脏读 不可重复读,幻读
ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后另一个事务才能读取到,可以避免脏读.
ISOLATION_REPEATABLE_READ:数据库就是使用的这种隔离级别,可以避免脏读和不可重复读,但是可能出现 幻读(幻读:一个事务读取完,另一个事务提交了更新,本事务再次读取会 发现前后数据不一致,像产生了幻觉一样,所以叫幻读)
ISOLATION_SERIALIZABLE:花费代价最高也是最可开的事务隔离级别,事务被处理为顺序执行,但是这种隔离 级别会产生锁表,就是一个事务读取之后,另一个事务必须等待这个事务完成, 他才可以进行,第一个事务会将整张表锁起来,一般不会使用这种隔离级别, 性能极低!