spring boot 事务使用究极指南

 

 spring事务有编程式事务,和声明式事务

1 声明式事务:

  1. 配置文件的方式,即在spring xml文件中进行统一配置,开发者基本上就不用关注事务的事情了,代码中无需关心任何和事务相关的代码,一切交给spring处理。

  2. 注解的方式,只需在需要spring来帮忙管理事务的方法上加上@Transaction注解就可以了,注解的方式相对来说更简洁一些,都需要开发者自己去进行配置。

1、启用Spring的注释驱动事务管理功能

在spring配置类上加上@EnableTransactionManagement注解

@EnableTransactionManagement
public class MainConfig4 {
}

当spring容器启动的时候,发现有@EnableTransactionManagement注解,此时会拦截所有bean的创建,扫描看一下bean上是否有@Transaction注解(类、或者父类、或者接口、或者方法中有这个注解都可以),如果有这个注解,spring会通过aop的方式给bean生成代理对象,代理对象中会增加一个拦截器,拦截器会拦截bean中public方法执行,会在方法执行之前启动事务,方法执行完毕之后提交或者回滚事务。

EnableTransactionManagement 的源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

 /**
  * spring是通过aop的方式对bean创建代理对象来实现事务管理的
  * 创建代理对象有2种方式,jdk动态代理和cglib代理
  * proxyTargetClass:为true的时候,就是强制使用cglib来创建代理
  */
 boolean proxyTargetClass() default false;

 /**
  * 用来指定事务拦截器的顺序
  * 我们知道一个方法上可以添加很多拦截器,拦截器是可以指定顺序的
  * 比如你可以自定义一些拦截器,放在事务拦截器之前或者之后执行,就可以通过order来控制
  */
 int order() default Ordered.LOWEST_PRECEDENCE;
}

2、定义事务管理器

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

3、需使用事务的目标上加@Transaction注解

  • @Transaction放在接口上,那么接口的实现类中所有public都被spring自动加上事务

  • @Transaction放在类上,那么当前类以及其下无限级子类中所有pubilc方法将被spring自动加上事务

  • @Transaction放在public方法上,那么该方法将被spring自动加上事务

  • 注意:@Transaction只对public方法有效

 @Transactional源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    /**
     * 指定事务管理器的bean名称,如果容器中有多事务管理器PlatformTransactionManager,
     * 那么你得告诉spring,当前配置需要使用哪个事务管理器
     */
    @AliasFor("transactionManager")
    String value() default "";

    /**
     * 同value,value和transactionManager选配一个就行,也可以为空,如果为空,默认会从容器中按照类型查找一个事务管理器bean
     */
    @AliasFor("value")
    String transactionManager() default "";

    /**
     * 事务的传播属性
     */
    Propagation propagation() default Propagation.REQUIRED;

    /**
     * 事务的隔离级别,就是制定数据库的隔离级别,数据库隔离级别大家知道么?不知道的可以去补一下
     */
    Isolation isolation() default Isolation.DEFAULT;

    /**
     * 事务执行的超时时间(秒),执行一个方法,比如有问题,那我不可能等你一天吧,可能最多我只能等你10秒
     * 10秒后,还没有执行完毕,就弹出一个超时异常吧
     */
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    /**
     * 是否是只读事务,比如某个方法中只有查询操作,我们可以指定事务是只读的
     * 设置了这个参数,可能数据库会做一些性能优化,提升查询速度
     */
    boolean readOnly() default false;

    /**
     * 定义零(0)个或更多异常类,这些异常类必须是Throwable的子类,当方法抛出这些异常及其子类异常的时候,spring会让事务回滚
     * 如果不配做,那么默认会在 RuntimeException 或者 Error 情况下,事务才会回滚 
     */
    Class<? extends Throwable>[] rollbackFor() default {};

    /**
     * 和 rollbackFor 作用一样,只是这个地方使用的是类名
     */
    String[] rollbackForClassName() default {};

    /**
     * 定义零(0)个或更多异常类,这些异常类必须是Throwable的子类,当方法抛出这些异常的时候,事务不会回滚
     */
    Class<? extends Throwable>[] noRollbackFor() default {};

    /**
     * 和 noRollbackFor 作用一样,只是这个地方使用的是类名
     */
    String[] noRollbackForClassName() default {};

}

 要点注意:通常我们使用springboot不使用@EnableTransactionManagement就能使用事务,也就是上述的1,2步骤是不需要的。

原因是@SpringBootApplication,这个注解,帮我们自动开启了事务管理器的配置,详情情况点这里@SpringBootApplication配置解析

2 编程式事务:基本写法

  • 方式1:通过PlatformTransactionManager控制事务

  • 方式2:通过TransactionTemplate控制事务

步骤1:定义事务管理器PlatformTransactionManager

  • PlatformTransactionManager多个实现类,用来应对不同的环境

    图片

    JpaTransactionManager:如果你用jpa来操作db,那么需要用这个管理器来帮你控制事务。

    DataSourceTransactionManager:如果你用是指定数据源的方式,比如操作数据库用的是:JdbcTemplate、mybatis、ibatis,那么需要用这个管理器来帮你控制事务。

    HibernateTransactionManager:如果你用hibernate来操作db,那么需要用这个管理器来帮你控制事务。

    JtaTransactionManager:如果你用的是java中的jta来操作db,这种通常是分布式事务,此时需要用这种管理器来控制事务。

步骤2:定义事务属性TransactionDefinition

    定义事务属性,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。

     spring中使用TransactionDefinition接口来表示事务的定义信息,有个子类比较常用:DefaultTransactionDefinition。

步骤3,4,5开启事务,执行业务,回滚或者提交事务

@Test
public void test1() throws Exception {
    //定义一个数据源
    org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
    dataSource.setUsername("root");
    dataSource.setPassword("root123");
    dataSource.setInitialSize(5);
    //定义一个JdbcTemplate,用来方便执行数据库增删改查
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    //1.定义事务管理器,给其指定一个数据源(可以把事务管理器想象为一个人,这个人来负责事务的控制操作)
    PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
    //2.定义事务属性:TransactionDefinition,TransactionDefinition可以用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。
    TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    //3.开启事务:调用platformTransactionManager.getTransaction开启事务操作,得到事务状态(TransactionStatus)对象
    TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
    //4.执行业务操作,下面就执行2个插入操作
    try {
        System.out.println("before:" + jdbcTemplate.queryForList("SELECT * from t_user"));
        jdbcTemplate.update("insert into t_user (name) values (?)", "test1-1");
        jdbcTemplate.update("insert into t_user (name) values (?)", "test1-2");
        //5.提交事务:platformTransactionManager.commit
        platformTransactionManager.commit(transactionStatus);
    } catch (Exception e) {
        //6.回滚事务:platformTransactionManager.rollback
        platformTransactionManager.rollback(transactionStatus);
    }
    System.out.println("after:" + jdbcTemplate.queryForList("SELECT * from t_user"));
}

通过TransactionTemplate控制事务

配置类,将事务管理器PlatformTransactionManager、事务模板TransactionTemplate都注册到spring中


@Configuration
@ComponentScan
public class MainConfig3 {
    @Bean
    public DataSource dataSource() {
        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
        dataSource.setUsername("root");
        dataSource.setPassword("root123");
        dataSource.setInitialSize(5);
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager);
    }
}

 实际业务调用:

@Component
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private TransactionTemplate transactionTemplate;

    //模拟业务操作1
    public void bus1() {
        this.transactionTemplate.executeWithoutResult(transactionStatus -> {
            //先删除表数据
            this.jdbcTemplate.update("delete from t_user");
            //调用bus2
            this.bus2();
        });
    }

    //模拟业务操作2
    public void bus2() {
        this.transactionTemplate.executeWithoutResult(transactionStatus -> {
            this.jdbcTemplate.update("insert into t_user (name) VALUE (?)", "java");
            this.jdbcTemplate.update("insert into t_user (name) VALUE (?)", "spring");
            this.jdbcTemplate.update("insert into t_user (name) VALUE (?)", "mybatis");
        });
    }

    //查询表中所有数据
    public List userList() {
        return jdbcTemplate.queryForList("select * from t_user");
    }
}

3.springboot开启全局事务配置的方法

@Configuration
public class TransactionAdviceConfig {

    private static final String AOP_POINTCUT_EXPRESSION = "execution(* com.platform.service.impl.*.*(..))";

    @Resource
    private PlatformTransactionManager transactionManager;

    @Bean
    @SuppressWarnings("deprecation")
    public TransactionInterceptor txAdvice() {

        DefaultTransactionAttribute txAttrRequired = new DefaultTransactionAttribute();
        txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        DefaultTransactionAttribute txAttrRequiredReadonly = new DefaultTransactionAttribute();
        txAttrRequiredReadonly.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        txAttrRequiredReadonly.setReadOnly(false);

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

        source.addTransactionalMethod("save*", txAttrRequired);
        source.addTransactionalMethod("add*", txAttrRequired);
        source.addTransactionalMethod("create*", txAttrRequired);
        source.addTransactionalMethod("update*", txAttrRequired);
        source.addTransactionalMethod("get*", txAttrRequiredReadonly);
        source.addTransactionalMethod("query*", txAttrRequiredReadonly);
        source.addTransactionalMethod("find*", txAttrRequiredReadonly);
        source.addTransactionalMethod("*", txAttrRequiredReadonly);

        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值