Spring @Transactional注解

为什么要写这篇文章?

你是不是和我一样,每次想要在代码中使用事务回滚的时候就想起来有这么一个@Transacitonal注解,但是隐隐约约记得注解有好几种参数,不知道该选哪一种,也不知道什么地方应该添加这个注解。

于是乎,你和我一样打开了百度开始搜索@Transactional注解,如果是这样的话,建议收藏这篇文章,我会干练的罗列哪里用@Transactional@Transactional不同参数的区别@Transactional使用的注意事项~

1.哪里用@Transactional

  • 不要在private 级别的方法上使用@Transactional注解
  • 不要在final、static修饰方法上使用@Transactional注解
  • 不推荐直接在类上使用@Transactional注解,范围太大没必要

建议在需要事务回滚的Service类中,被Controller或其他Service调用的public且非final和static的方法上添加@Transactional注解 ~

2.Transactional不同参数的区别

@Transactional注解有个关键的参数propagation,它控制着事务的传播行为:

propagation 支持 7 种事务传播特性:

  • REQUIRED默认的传播行为,如果当前没有事务,则创建一个新事务;如果存在事务,则加入当前事务。
  • MANDATORY:支持当前事务,如果不存在则抛出异常
  • NEVER:非事务性执行,如果存在事务,则抛出异常
  • REQUIRES_NEW:无论当前是否存在事务,都会创建一个新事务,原有事务被挂起。
  • NESTED:嵌套事务,被调用方法在一个嵌套的事务中运行,这个事务依赖于当前的事务。
  • SUPPORTS:如果当前存在事务,则加入;如果没有,就以非事务方式执行。
  • NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,将其挂起。

REQUIRE:调用方没有事务就新建事务,有就加入调用方事务中

@Service
public class ServiceA {

    @Resource
    private ServiceC serviceC;
 
    @Transactional
    public void testA() {
        invokeDataBase();
        testA();
        serviceC.testC();
    }

	private void testB() {
        invokeDataBase();
	}

}


@Service
public class ServiceC {

    @Transactional
    public void testC() {
        invokeDataBase();
    }

}

A方法开启事务称之为A事务,B方法未开启事务所以AB方法同一个事务,C方法的事务则加入当前A事务 。

MANDATORY: 调用方没事务抛异常,有事务就加入调用方事务

@Service
public class ServiceA {

    @Resource
    private ServiceC serviceC;
 
    @Transactional
    public void testA() {
        invokeDataBase();
        serviceC.testC();
    }

	private void testB() {
        invokeDataBase();
        serviceC.testC();
	}

}


@Service
public class ServiceC {

    @Transactional(propagation = Propagation.MANDATORY)
    public void testC() {
        invokeDataBase();
    }

}

A方法有事务,调用C方法:C事务加入A事务

B方法没事务,调用C方法:C方法直接一个异常的抛出!

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory' 

NEVER: 调用方有事务抛异常,调用方无事务自身也没事务

@Service
public class ServiceA {

    @Resource
    private ServiceC serviceC;
 
    @Transactional
    public void testA() {
        invokeDataBase();
        serviceC.testC();
    }

	private void testB() {
        invokeDataBase();
        serviceC.testC();
	}

}


@Service
public class ServiceC {

    @Transactional(propagation = Propagation.NEVER)
    public void testC() {
        invokeDataBase();
    }

}

B方法没有事务,调用C方法:则B和C都是没事务的普通方法

A方法有事务,调用C方法:则C方法直接一个异常的抛出! 

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

REQUIRES_NEW:创建一个新事务,独立于调用方的事务,互不影响

@Service
public class ServiceA {

    @Resource
    private ServiceC serviceC;
 
    @Transactional
    public void testA() {
        invokeDataBase();
        serviceC.testC();
    }

	private void testB() {
        invokeDataBase();
        serviceC.testC();
	}

}


@Service
public class ServiceC {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void testC() {
        invokeDataBase();
    }

}

A方法有事务,调用C方法:C事务会成为一个新的事务,A事务不会因为C方法的失败而回滚,也就是A和C互为两个独立的事务。

B方法没有事务,调用C方法:此时REQUIRES_NEW类似于REQUIRE,创建了一个新的事务C

NESTED:调用方有事务则创建一个嵌套子事务,子事务不影响调用方事务和同级事务,但调用方事务会影响子事务

@Service
public class ServiceA {

    @Resource
    private ServiceC serviceC;
 
    @Transactional
    public void testA() {
        invokeDataBase();
        serviceC.testC();
    }

	private void testB() {
        invokeDataBase();
        serviceC.testC();
	}

}


@Service
public class ServiceC {

    @Transactional(propagation = Propagation.NESTED)
    public void testC() {
        invokeDataBase();
    }

}

A方法有事务,调用C方法:C方法会生成一个嵌套子事务,简而言之外部事务会影响子事务,但子事务的回滚不会影响外部事务,也不会影响同级的其它事务

B方法没有事务,调用C方法:此时NESTED类似于REQUIRE,创建了一个新的事务C

NOT_SUPPORTED: 此方法相当于未开启事务,且此方法的异常不会导致调用方事务回滚

@Service
public class ServiceA {

    @Resource
    private ServiceC serviceC;
 
    @Transactional
    public void testA() {
        invokeDataBase();
        serviceC.testC();
    }

	private void testB() {
        invokeDataBase();
        serviceC.testC();
	}

}


@Service
public class ServiceC {

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void testC() {
        invokeDataBase();
    }

}

 A方法有事务,调用C方法:C方法将以非事务的形式运行,也就是说C方法的异常不会回滚C方法,同时也不会使A事务回滚

B方法没有事务,调用C方法:相当于完全没有启用事务

SUPPORTS:调用方有事务则加入调用方事务中,调用方无事务则不启用事务

@Service
public class ServiceA {

    @Resource
    private ServiceC serviceC;
 
    @Transactional
    public void testA() {
        invokeDataBase();
        serviceC.testC();
    }

	private void testB() {
        invokeDataBase();
        serviceC.testC();
	}

}


@Service
public class ServiceC {

    @Transactional(propagation = Propagation.SUPPORTS)
    public void testC() {
        invokeDataBase();
    }

}

 A方法有事务,调用C方法:C事务将加入调用方的A事务中

B方法没有事务,调用C方法:C方法将不会启用事务

3.@Transactional使用注意事项

  • @Transactional注解默认捕获RuntimeException和其子类以及Error类型异常
  • try-catch语句中若catch了Exception又不抛出,则事务无法捕获异常并回滚
  • 事务具有传播性,即使调用方(启用事务)try-catch了全部异常,但被调用方法(启用事务)抛出异常,事务依旧可以传播给调用方进行回滚
  • 检查类异常抛出,事务是不会捕捉到的,要想捕捉需要启用 rollbackFor 参数
  • 需要数据库引擎支持事务:例如Mysql MyISAM不支持,InnoDB支持
  • 同类方法调用是不会经过代理的,因为这种调用直接通过 this 对象进行,不经过 AOP 的切面
public void test() {
    invokeDataBase();
    a();
    b();
}
 
public void a() {
    invokeDataBase();
}
 
@Transactional
public void b() {
    invokeDataBase();
}
  • @Transactional 注解通过 AOP 来管理事务,而 AOP 依赖于代理机制。因此,Bean 必须由Spring管理实例
  • 在多线程环境下,Spring 的事务管理器不会跨线程传播事务,事务的状态(如事务是否已开启)是存储在线程本地的 ThreadLocal 来存储和管理事务上下文信息。这意味着每个线程都有一个独立的事务上下文,事务信息在不同线程之间不会共享
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值