spring事务的传播特性
Posted hhhshct
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring事务的传播特性相关的知识,希望对你有一定的参考价值。
spring事务分为本地事务和分布式事务,其中本地事务其实就是数据库事务,Spring事务有三个核心类:TransactionDefinition、PlatformTransactionManager、TransactionStatus。
首先来看事务定义类TransactionDefinition,其中定义了事务的7种传播特性和5种隔离级别:
PROPAGATION_REQUIRED 需要事务,如果当前没有事务则新建一个事务
PROPAGATION_SUPPORTS 支持事务,如果当前有事务则加入到这个事务,没有则以非事务的方式运行,默认值
PROPAGATION_MANDATORY 需要事务,如果当前没有事务则抛出异常
PROPAGATION_REQUIRES_NEW 开启一个新事物,如果当前有事务则挂起
PROPAGATION_NOT_SUPPORTED 不支持事务,总是以非事务的方式运行
PROPAGATION_NEVER 不支持事务,如果当前有事务则抛出异常
PROPAGATION_NESTED 嵌套事务
ISOLATION_DEFAULT 默认隔离级别,取决于本地数据库设置的隔离级别
ISOLATION_READ_UNCOMMITTED 读未提交
ISOLATION_READ_COMMITTED 读已提交
ISOLATION_REPEATABLE_READ 可重复读
ISOLATION_SERIALIZABLE 串行化
接着看PlatformTransactionManager接口,它定义了spring事务的基本操作,随着我们使用的orm框架的不同对应有不同的实现类
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; 获取事务
void commit(TransactionStatus status) throws TransactionException; 提交事务
void rollback(TransactionStatus status) throws TransactionException;回滚事务
随着我们使用的orm框架的不同对应有不同的实现类,例如spring jdbc的DataSourceTransactionManager,jpa的JpaTransactionManager等
最后一个TransactionStatus,定义了事务的4中状态
boolean isNewTransaction(); 是否为新事物
boolean hasSavepoint(); 是否有保存点
boolean isRollbackOnly(); 是否只回滚
boolean isCompleted() 是否完成
在日常开发中,我们常用到的传播特性就是PROPAGATION_SUPPORTS、PROPAGATION_REQUIRED和PROPAGATION_REQUIRES_NEW,隔离级别一般为默认值,而readOnly则是查询语句为ture,其他为false。下面来测一下三种传播行为下的事务是如何执行的,方便期间,使用springboot构建项目,加入jdbc的starter依赖
新建两个测试表,语句如下:
CREATE TABLE role ( id integer NOT NULL, rolename character varying(4) COLLATE pg_catalog."default", CONSTRAINT role_pkey PRIMARY KEY (id) ) CREATE TABLE txuser ( id integer NOT NULL, name character varying(5) COLLATE pg_catalog."default", CONSTRAINT txuser_pkey PRIMARY KEY (id) )
使用postgresql数据库,加入配置
spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/test01 spring.datasource.username=postgres spring.datasource.password=123456
dao层
package com.example.tx.dao; import com.example.tx.model.Role; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class RoleReposity { @Autowired private JdbcTemplate jdbcTemplate; public int addRole(Role role){ return jdbcTemplate.update("insert into role(id,rolename) values (?,?)",role.getId(),role.getRolename()); } }
package com.example.tx.dao; import com.example.tx.model.Txuser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class TxuserReposity { @Autowired private JdbcTemplate jdbcTemplate; public int addUser(Txuser user){ return jdbcTemplate.update("insert into txuser(id,name) values(?,?)",user.getId(),user.getName()); } }
service层
package com.example.tx.service;
import com.example.tx.dao.RoleReposity;
import com.example.tx.model.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class RoleService {
@Autowired
private RoleReposity roleReposity;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addRole(Role role1,Role role2){
roleReposity.addRole(role1);
roleReposity.addRole(role2);
}
}
package com.example.tx.service;
import com.example.tx.dao.TxuserReposity;
import com.example.tx.model.Role;
import com.example.tx.model.Txuser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private RoleService roleService;
@Autowired
private TxuserReposity txuserReposity;
@Transactional(propagation = Propagation.REQUIRED)
public void addUser (Txuser user1, Txuser user2,Role role1,Role role2){
txuserReposity.addUser(user1);
roleService.addRole(role1,role2);
txuserReposity.addUser(user2);
}
}
测试代码
package com.example.tx;
import com.example.tx.model.Role;
import com.example.tx.model.Txuser;
import com.example.tx.service.UserService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TxApplicationTests {
@Autowired
private UserService userService;
@Test
public void contextLoads() {
Txuser user1 = new Txuser();
user1.setId(1);
user1.setName("mike");
Txuser user2 = new Txuser();
user2.setId(2);
user2.setName("zhangsan");
Role role1 = new Role();
role1.setId(1);
role1.setRolename("aaa");
Role role2 = new Role();
role2.setId(2);
role2.setRolename("bbb");
userService.addUser(user1,user2, role1,role2);
}
}
在测试代码中将user2的name超过限制(txuser中设置name长度为5),其它实体正常,测试如下:
1、当RoleService方法上事务传播特性为SUPPORTS,测试结果为全部回滚。
2、当RoleService方法上事务传播特性为REQUIRED,测试结果为全部回滚。
3、当RoleService方法上事务传播特性为REQUIRES_NEW,测试结果为两条role记录添加成功。
修改user2的name为"asd",role2中name为"useradmin"(超出role中限制)
1、当RoleService方法上事务传播特性为SUPPORTS,测试结果为全部回滚。
2、当RoleService方法上事务传播特性为REQUIRED,测试结果为全部回滚。
3、当RoleService方法上事务传播特性为REQUIRES_NEW,测试结果为全部回滚。
从以上结果可以看出当内层事务的传播特性为SUPPORTS或者REQUIRED时,只要发生异常就全部回滚,这是因为两个事务获取的是同一个数据库连接;当内层事务的传播特性为REQUIRES_NEW时,当外层事务发生异常时,内层事务不受影响,当内层事务发生异常,则全部回滚,此时两个事务获取的是不同的数据库连接对象,内层事务出现异常是会传播给外层,而外层事务异常时,内层事务则不受影响。
此外我们在使用Spring事务是一定要注意同类间的方法调用,如果是直接调用,则被调用方法的事务是不起作用的,这是因为此时为对象内的调用,并不会使用spring为我们创建的代理对象,此时需要我们在调用方法内获取代理对象,使用代理对象去调用同类中的其它事务方法,如下:
public void testPropagation(Txuser user, Role role){ System.out.println(1231231); UserService userService = (UserService)AopContext.currentProxy(); userService.addUser(user1,user2, role1,role2); }
以上是关于spring事务的传播特性的主要内容,如果未能解决你的问题,请参考以下文章