Java架构师—详解事务的传播

前言

       事务传播的七种方式;测试事务传播的方式;使用Junit进行单元测试;为何不使用@EnableTransactionManagement就能使用事务?


文章目录

  • 前言
  • 一、事务的传播方式
    • 1.1 事务传播的七种方式
    • 1.2 测试事务传播的方式
  • 三、使用Junit进行单元测试
    • 3.1 在对应的Module下的pom.xml中引入Junit依赖:
    • 3.2 单元测试类TransTest :
  • 四、为何不使用@EnableTransactionManagement就能使用事务?
  • 总结


一、事务的传播方式

1.1 事务传播的七种方式

       事务传播 - Propagation

  • REQUIRED:
    使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的;
    如果当前存在事务,则加入这个事务,成为一个整体。
    举例:领导没饭吃,我有钱,我会自己买了自己吃;领导有的吃,会分给你一起吃。
  • SUPPORTS:
    如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。
    举例:领导没饭吃,我也没饭吃;领导有饭吃,我也有饭吃。
  • MANDATORY:
    该传播属性强制必须存在一个事务,如果不存在,则抛出异常。
    举例:领导必须管饭,不管饭没饭吃,我就不乐意了,就不干了(抛出异常)。
  • REQUIRES_NEW:
    如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;
    如果当前没有事务,则同 REQUIRED。
    举例:领导有饭吃,我偏不要,我自己买了自己吃。
  • NOT_SUPPORTED:
    如果当前有事务,则把事务挂起,自己不适用事务去运行数据库操作。
    举例:领导有饭吃,分一点给你,我太忙了,放一边,我不吃。
  • NEVER:
    如果当前有事务存在,则抛出异常。
    举例:领导有饭给你吃,我不想吃,我热爱工作,我抛出异常。
  • NESTED:
    如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;
    如果当前没有事务,则同 REQUIRED。
    但是如果主事务提交,则会携带子事务一起提交。
    如果主事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以回滚或不回滚。
    举例:领导决策不对,老板怪罪,领导带着小弟一同受罪。小弟出了差错,领导可以推卸责任。
public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

       事务注解@Transactional:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

1.2 测试事务传播的方式

       测试接口TestTransService :

public interface TestTransService {
    public void testPropagationTrans();
}

       测试接口实现类TestTransServiceImpl :

import com.imooc.service.StudentService;
import com.imooc.service.TestTransService;
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 TestTransServiceImpl implements TestTransService {

    @Autowired
    private StudentService studentService;

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void testPropagationTrans() {
        studentService.saveParent();

        try {
            // save point
            studentService.saveChildren();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // delete
        // update
        studentService.saveParent();

//        int a = 1 / 0;
    }
}

       测试业务接口StudentService :

public interface StudentService {

    public void saveParent();

    public void saveChildren();
}

       测试业务接口实现类StudentServiceImpl :

import com.imooc.mapper.StudentMapper;
import com.imooc.pojo.Student;
import com.imooc.service.StudentService;
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 StudentServiceImpl implements StudentService {

    // 开发工具问题:Settings->Editor->Code Style->inspections->Spring->Spring Core->Core->Autowiring for Bean Class 取消勾选
    @Autowired
    private StudentMapper studentMapper;
    
    @Override
    public void saveParent() {
        Student student = new Student();
        student.setName("parent");
        student.setAge(19);
        studentMapper.insert(student);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void saveChildren() {
        saveChild1();
        int a = 1 / 0;
        saveChild2();
    }

    public void saveChild1() {
        Student student1 = new Student();
        student1.setName("child-1");
        student1.setAge(11);
        studentMapper.insert(student1);
    }
    public void saveChild2() {
        Student student2 = new Student();
        student2.setName("child-2");
        student2.setAge(22);
        studentMapper.insert(student2);
    }
}

三、使用Junit进行单元测试

3.1 在对应的Module下的pom.xml中引入Junit依赖:

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

3.2 单元测试类TransTest :

import com.imooc.Application;
import com.imooc.service.TestTransService;
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(classes = Application.class)
public class TransTest {


    @Autowired
    private TestTransService testTransService;

    @Test
    public void myTest() {
        testTransService.testPropagationTrans();
    }

}

四、为何不使用@EnableTransactionManagement就能使用事务?

       在SpringBoot的自动装配机制中,已经开启了事务自动装配。
       META-INF\spring.factories中org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\

import java.util.Collection;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.support.TransactionTemplate;

@Configuration
@ConditionalOnClass({PlatformTransactionManager.class})
@AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
@EnableConfigurationProperties({TransactionProperties.class})
public class TransactionAutoConfiguration {
    public TransactionAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public TransactionManagerCustomizers platformTransactionManagerCustomizers(ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {
        return new TransactionManagerCustomizers((Collection)customizers.orderedStream().collect(Collectors.toList()));
    }

    @Configuration
    @ConditionalOnBean({PlatformTransactionManager.class})
    @ConditionalOnMissingBean({AbstractTransactionManagementConfiguration.class})
    public static class EnableTransactionManagementConfiguration {
        public EnableTransactionManagementConfiguration() {
        }

        @Configuration
        @EnableTransactionManagement(
            proxyTargetClass = true
        )
        @ConditionalOnProperty(
            prefix = "spring.aop",
            name = {"proxy-target-class"},
            havingValue = "true",
            matchIfMissing = true
        )
        public static class CglibAutoProxyConfiguration {
            public CglibAutoProxyConfiguration() {
            }
        }

        @Configuration
        @EnableTransactionManagement(
            proxyTargetClass = false
        )
        @ConditionalOnProperty(
            prefix = "spring.aop",
            name = {"proxy-target-class"},
            havingValue = "false",
            matchIfMissing = false
        )
        public static class JdkDynamicAutoProxyConfiguration {
            public JdkDynamicAutoProxyConfiguration() {
            }
        }
    }

    @Configuration
    @ConditionalOnSingleCandidate(PlatformTransactionManager.class)
    public static class TransactionTemplateConfiguration {
        private final PlatformTransactionManager transactionManager;

        public TransactionTemplateConfiguration(PlatformTransactionManager transactionManager) {
            this.transactionManager = transactionManager;
        }

        @Bean
        @ConditionalOnMissingBean
        public TransactionTemplate transactionTemplate() {
            return new TransactionTemplate(this.transactionManager);
        }
    }
}

总结

事务传播的七种方式;测试事务传播的方式;使用Junit进行单元测试;为何不使用@EnableTransactionManagement就能使用事务?

你可能感兴趣的:(Java架构师之路,java,架构,数据库,后端)