zoukankan      html  css  js  c++  java
  • 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);
        }

      

  • 相关阅读:
    免费素材下载:淡蓝色的PSD格式UI套件
    分享一个CSS3的网格系统架构 ResponsiveAeon
    最新收集的超棒Mobile/Web UI和用户体验设计
    一个帮助你针对不同标签自动填入内容的轻量级javascript类库 fixiejs
    发现任何VB函数、插件、甚至按键精灵对“文件下载”窗口后台失效
    android 界面 滑入 效果
    分布式HeadLoop
    VB ListView
    android 下载保存图片
    网址
  • 原文地址:https://www.cnblogs.com/hhhshct/p/10638489.html
Copyright © 2011-2022 走看看