参考链接
https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction
如何将 @Transactional 事务注解运用到炉火纯青?
Most users prefer declarative transaction management, which we recommend in most cases.
大多数用户更喜欢声明式事务管理,这是我们在大多数情况下推荐的。
声明式事务(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注解修饰的方法,否则事务不生效。
总结
- 使用 Transactional 方法的修饰符必须是 public ,失效的修饰符有private,无修饰符,protected.
- 不要在同一个类中调用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);
}
}