readOnly
:事务是否是只读的,默认是 false。对于只读查询,可以指定事务类型为 readonly,即只读事务。由于只读事务不存在数据的修改, 因此数据库将会为只读事务提供一些优化手段
rollbackFor
:设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则事务回滚
noRollbackFor
:设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚
rollbackForClassName
:设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。
noRollbackForClassName
:设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。
使用@Transactional需要注意的地方
@Transactional 只能应用到 public 方法才有效
在默认配置中,Spring FrameWork 的事务框架代码只会将出现 runtime, unchecked 异常的事务标记为回滚;也就是说事务中抛出的异常是 RuntimeException 或其子类,这样事务才会回滚(默认情况下 Error 也会导致事务回滚)。但是,在默认配置的情况下,所有的 checked 异常都不会引起事务回滚。
1、创建spring项目
新建Spring Initializr项目
配置pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
创建entity包,创建对应数据库表的实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "extra_ad")
public class ExtraAd {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name ="id",nullable = false)
private Long id;
@Basic
@Column(name = "name",nullable = false)
private String name;
public ExtraAd(String name){
this.name = name;
创建dao包,创建操作数据库表的类的接口
public interface ExtraAdDao extends JpaRepository<ExtraAd,Long> {
创建exception包,自定义异常类
public class CustomException extends Exception{
public CustomException(String message){
super(message);
创建service包,定义服务接口
public interface ISpringTransaction {
void CatchExceptionCanNotRollback();
void NotRuntimeExceptionCanNotRollback() throws CustomException;
void RuntimeExceptionCanRollback();
void AssignExceptionCanRollback() throws CustomException;
void RollbackOnlyCanRollback() throws CustomException;
void NonTransactionalCanNotRollback();
在service包下创建impl包,实现ISpringTransaction接口
在test下新建测试类
整体项目结构如图:
2、创建测试数据库
CREATE DATABASE IF NOT EXISTS `example`;
CREATE TABLE IF NOT EXISTS `example`.`extra_ad` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`name` varchar(48) NOT NULL COMMENT '名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COMMENT='额外的表';
TRUNCATE TABLE extra_ad;
对应的application.yml
spring:
profiles:
active: dev
open-in-view: false
datasource:
url: jdbc:mysql://127.0.0.1:3306/example?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false
username: ****
password: ****
driver-class-name: com.mysql.cj.jdbc.Driver
tomcat:
max-active: 4
min-idle: 2
initial-size: 2
spring:
profiles: dev
server:
port: 8080
spring:
profiles: test
server:
port: 8081
spring:
profiles: prod
server:
port: 8082
几种实践情况
1、主动捕获异常,事务不能回滚
@Override
@Transactional
public void CatchExceptionCanNotRollback() {
try{
extraAdDao.save(new ExtraAd("test1"));
throw new RuntimeException();
}catch (Exception ex){
ex.printStackTrace();
执行前,表中无记录
@Test
public void NotRuntimeExceptionCanNotRollback() throws CustomException{
springTransaction.NotRuntimeExceptionCanNotRollback();
并没有回滚,数据插入成功
清空数据库,继续下一个
2、不是unchecked异常,事务不能回滚
@Override
@Transactional
public void NotRuntimeExceptionCanNotRollback() throws CustomException {
try {
extraAdDao.save(new ExtraAd("test2"));
throw new RuntimeException();
} catch (Exception ex){
throw new CustomException(ex.getMessage());
这里强制把RumtimeException转为CustomException,就不是unchecked异常了
没有回滚,成功插入
清空数据库,继续下一条
3、uncheck异常,事务可以回滚
@Override
@Transactional
public void RuntimeExceptionCanRollback() {
extraAdDao.save(new ExtraAd("test3"));
throw new RuntimeException();
RuntimeException是uncheck异常,发生回滚,数据并没有插入成功
4、指定异常,事务可以回滚
@Override
@Transactional(rollbackFor = {CustomException.class})
public void AssignExceptionCanRollback() throws CustomException {
try{
extraAdDao.save(new ExtraAd("test4"));
throw new RuntimeException();
}catch (Exception ex){
throw new CustomException(ex.getMessage());
参数rollbackFor指定了CustomException异常类型,并且抛出了该异常,事务回滚,数据插入失败
5、Rollback Only,事务处理失败
@Transactional
public void oneSaveMethod(){
extraAdDao.save(new ExtraAd("test5"));
@Override
@Transactional
public void RollbackOnlyCanRollback() throws CustomException {
oneSaveMethod();
try{
extraAdDao.save(new ExtraAd());
}catch (Exception ex){
ex.printStackTrace();
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.ad.extraad.entity.ExtraAd.name; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : com.ad.extraad.entity.ExtraAd.name
由于字段name要求不为空,extraAdDao.save(new ExtraAd())
运行出错
org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
本方法中调用了标记为事务的方法oneSaveMethod(),多个事务合为一个事务,只有全部正确执行完成才会提交事务,由于extraAdDao.save(new ExtraAd())
运行出错,事务被标记为rollback-only,导致事务回滚,数据插入失败。
6、同一个类中,一个不标注事务的方法调用了标注了事务的方法,事务会失效
@Transactional
public void anotherOneSaveMethod(){
extraAdDao.save(new ExtraAd("test6"));
throw new RuntimeException();
@Override
public void NonTransactionalCanNotRollback() {
anotherOneSaveMethod();
可以看到,事务并没有回滚,数据成功插入
运行中抛出的异常
java.lang.RuntimeException
at com.ad.extraad.service.impl.SpringTransactionImpl.anotherOneSaveMethod(SpringTransactionImpl.java:89)
at com.ad.extraad.service.impl.SpringTransactionImpl.NonTransactionalCanNotRollback(SpringTransactionImpl.java:93)
at com.ad.extraad.service.impl.SpringTransactionImpl$$FastClassBySpringCGLIB$$3ffea55.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at com.ad.extraad.service.impl.SpringTransactionImpl$$EnhancerBySpringCGLIB$$175ae292.NonTransactionalCanNotRollback(<generated>)
at com.ad.extraad.service.TransactionTest.NonTransactionalCanNotRollback(TransactionTest.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
从抛出的异常可以看到,首先,我们拿到的是代理对象,调用 NonTransactionalCanNotRollback 方法。但是 NonTransactionalCanNotRollback 方法在调用 anotherOneSaveMethod 的时候却是原始对象的 anotherOneSaveMethod。所以,这里的调用根本就没有事务的存在,导致事务失效,就更加不存在回滚了.
7、不同类中,一个不标注事务的方法调用了标注了事务的方法,事务生效
可以看到,由于异常是uncheck类型,事务发生了回滚,数据并没有插入成功。
java.lang.RuntimeException
at com.ad.extraad.service.impl.SpringTransactionImpl.anotherOneSaveMethod(SpringTransactionImpl.java:89)
at com.ad.extraad.service.impl.SpringTransactionImpl$$FastClassBySpringCGLIB$$3ffea55.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
从抛出的异常可以看到,首先,我们拿到的是代理对象,再去调用anotherOneSaveMethod。所以,这就有事务了,即事务不会失效。
bkpp976
Java后端工程师
粉丝