spring事務分爲本地事務和分佈式事務,其中本地事務其實就是數據庫事務,Spring事務有三個核心類:TransactionDefinition、PlatformTransactionManager、TransactionStatus。spring
首先來看事務定義類TransactionDefinition,其中定義了事務的7種傳播特性和5種隔離級別:sql
PROPAGATION_REQUIRED 須要事務,若是當前沒有事務則新建一個事務數據庫
PROPAGATION_SUPPORTS 支持事務,若是當前有事務則加入到這個事務,沒有則以非事務的方式運行,默認值springboot
PROPAGATION_MANDATORY 須要事務,若是當前沒有事務則拋出異常框架
PROPAGATION_REQUIRES_NEW 開啓一個新事物,若是當前有事務則掛起分佈式
PROPAGATION_NOT_SUPPORTED 不支持事務,老是以非事務的方式運行post
PROPAGATION_NEVER 不支持事務,若是當前有事務則拋出異常測試
PROPAGATION_NESTED 嵌套事務url
ISOLATION_DEFAULT 默認隔離級別,取決於本地數據庫設置的隔離級別spa
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),其它實體正常,測試以下:
一、當RoleService方法上事務傳播特性爲SUPPORTS,測試結果爲所有回滾。
二、當RoleService方法上事務傳播特性爲REQUIRED,測試結果爲所有回滾。
三、當RoleService方法上事務傳播特性爲REQUIRES_NEW,測試結果爲兩條role記錄添加成功。
修改user2的name爲"asd",role2中name爲"useradmin"(超出role中限制)
一、當RoleService方法上事務傳播特性爲SUPPORTS,測試結果爲所有回滾。
二、當RoleService方法上事務傳播特性爲REQUIRED,測試結果爲所有回滾。
三、當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); }