spring事務的傳播特性

  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);
    }
相關文章
相關標籤/搜索