深入浅出Spring事务

参考链接

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction

如何将 @Transactional 事务注解运用到炉火纯青?

聊聊spring事务失效的12种场景,太坑了

Most users prefer declarative transaction management, which we recommend in most cases.
大多数用户更喜欢声明式事务管理,这是我们在大多数情况下推荐的。

声明式事务(transaction-declarative)

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-declarative

事务相关

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。

Transactional注解源码如下

package org.springframework.transaction.annotation;

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;
import org.springframework.transaction.TransactionDefinition;

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

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

	String[] label() default {};

	Propagation propagation() default Propagation.REQUIRED;

	
	Isolation isolation() default Isolation.DEFAULT;

	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	String timeoutString() default "";

	boolean readOnly() default false;

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

	String[] rollbackForClassName() default {};

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

	String[] noRollbackForClassName() default {};
}

同包类情况

在这里插入图片描述
前置知识 @AliasFor

注解注释翻译

描述单个方法或类的事务属性。

在类级别,此注释默认应用于声明类及其子类的所有方法。

请注意,它不适用于类层次结构中的祖先类;方法需要在本地重新声明才能参与子类级别的注释。

这种注解类型一般可以直接媲美Spring的org.springframework.transaction.interceptor.RuleBasedTransactionAttribute类,实际上AnnotationTransactionAttributeSource会直接将数据转换为后一个类,这样Spring的事务支持代码就不用知道注解了。

如果没有应用自定义回滚规则,事务将在 RuntimeException 和 Error 上回滚,但不会在检查的异常上回滚。

有关此注解属性语义的具体信息,请参阅 TransactionDefinition 和 org.springframework.transaction.interceptor.TransactionAttribute javadocs。
此注解通常与 org.springframework.transaction.PlatformTransactionManager 管理的线程绑定事务一起使用,将事务公开给当前执行线程中的所有数据访问操作。注意:这不会传播到方法中新启动的线程。
或者,此注解可以划分由 org.springframework.transaction.ReactiveTransactionManager 管理的反应式事务,该事务使用 Reactor 上下文而不是线程本地属性。

因此,所有参与的数据访问操作都需要在同一个反应式管道中的同一个 Reactor 上下文中执行。

作用范围

在这里插入图片描述
类、接口(包括注解类型)或枚举声明、方法声明
在这里插入图片描述

使用方法

写两个非常简单的方法,一个加注解,一个不加注解
在这里插入图片描述
在这里插入图片描述
根据数据库表可以知道回滚起作用了。

我们修改数据库引擎后进行测试

在这里插入图片描述
可以发现,数据没有回滚成功。
在这里插入图片描述
首先,回滚的前提是数据库支持事务, InnoDB是支持事务的, MyISAM是不支持事务的.

不同访问控制符的影响

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
根据测试结果可知,只有public 类型修饰的方法可以回滚,其他的都不行。

本类方法调用

我们使用test7方法来调用test4方法,执行后查看数据库结果。
在这里插入图片描述
可以看到,数据库中仍然成功插入了数据。
在这里插入图片描述
于是,我们将调用方法移到另一个类中重新测试回滚,
在这里插入图片描述
可以看到数据回滚成功没有插入。
在这里插入图片描述
于是我们得出结论,不要在同一个类中调用Transactional注解修饰的方法,否则事务不生效。

总结

  1. 使用 Transactional 方法的修饰符必须是 public ,失效的修饰符有private,无修饰符,protected.
  2. 不要在同一个类中调用Transactional注解修饰的方法,否则事务不生在这里插入代码片效。

失效

1.4.7 事务传播

https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#tx-propagation

编程式事务(Programmatic Transaction Management)

The Spring Framework provides two means of programmatic transaction management, by using:

  • The TransactionTemplate or TransactionalOperator.

  • A TransactionManager implementation directly.

The Spring team generally recommends the TransactionTemplate for programmatic transaction management in imperative flows and TransactionalOperator for reactive code.

TransactionTemplate

案例1

@RestController("tx")
public class TransactionManageController {

    private static final Logger logger = LoggerFactory.getLogger(TransactionManageController.class);

    private final TransactionTemplate transactionTemplate;
    private final DocMapper docMapper;

    public TransactionManageController(TransactionTemplate transactionTemplate, DocMapper docMapper) {
        this.transactionTemplate = transactionTemplate;
        this.docMapper = docMapper;
        this.templateInit();
    }

    /**
     * 属性设置
     */
    public void templateInit() {
        //隔离级别
        this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
        //超时时间
        this.transactionTemplate.setTimeout(30);
    }


    public int insert() {
        Doc doc = new Doc();
        String str = UUID.randomUUID().toString();
        logger.info("uuid:{}", str);
        doc.setDocName(str);
        doc.setCreateUserId(10086L);
        return docMapper.insert(doc);
    }

    public Object txInsertR() {
        return transactionTemplate.execute((TransactionCallback) callback -> {
            insert();
            return 1 / 0;
        });
    }

    public Object txInsertNoR() {
        return transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                insert();
                int a = 1 / 0;
            }
        });
    }


    /**
     * 有返回值
     */
    TransactionCallback callback = null;

    /**
     * 无返回值
     */
    TransactionCallbackWithoutResult result = null;

    @GetMapping("r")
    public Object r() {
        return txInsertR();
    }

    @GetMapping("noR")
    public void noR() {
        txInsertNoR();

    }

}

TransactionalOperator

TransactionManager

在这里插入图片描述

PlatformTransactionManager

public interface PlatformTransactionManager extends TransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;
							
	void commit(TransactionStatus status) throws TransactionException;
	
	void rollback(TransactionStatus status) throws TransactionException;
}

案例

@RestController("txManager")
public class TransactionManagerController {

    private static final Logger logger = LoggerFactory.getLogger(TransactionManagerController.class);

    private final PlatformTransactionManager transactionManager;
    private final DocMapper docMapper;

    public TransactionManagerController(PlatformTransactionManager transactionManager, DocMapper docMapper) {
        this.transactionManager = transactionManager;
        this.docMapper = docMapper;
    }

    @GetMapping("hello")
    public void hello() {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName("defName");
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            this.insert();
            int a = 1 / 0;
        } catch (Exception ex) {
            transactionManager.rollback(status);
            throw ex;
        }
        transactionManager.commit(status);
    }

    public int insert() {
        Doc doc = new Doc();
        String str = UUID.randomUUID().toString();
        logger.info("uuid:{}", str);
        doc.setDocName(str);
        doc.setCreateUserId(10086L);
        return docMapper.insert(doc);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值