将@Transactional定义在类和方法上有什么区别?

118

Case1

@Transactional
public class UserServiceImpl implements UserService {

    ...................
    public void method1(){
        try{
            method2();
        }catch(Exception e){

        }
    }
    public void method2(){

    }
}

案例2

public class UserServiceImpl implements UserService {

    ...................
    public void method1(){
        try{
            method2();
        }catch(Exception e){

        }
    }
    @Transactional
    public void method2(){

    }
}

如果发生任何异常,情况1会执行回滚操作,但是情况2不会执行。如果我遵循情况1,是否会有性能问题?

4个回答

141
情况1:@Transactional适用于每个公共方法,私有和受保护的方法被Spring忽略。
情况2:仅在method2()上应用@Transactional,而不是在method1()上应用。
情况1: - 调用method1() -> 开始一个事务。当method1()调用method2()时,不会开始新的事务,因为已经有一个事务。
情况2: - 调用method1() -> 不会开始任何事务。 当method1()调用method2()时会启动新事务,因为@Transactional在从同一类内调用方法时不起作用。 如果您从另一个类中调用method2(),则会起作用。
spring参考手册中:
在代理模式下(这是默认值),只有通过代理进行的外部方法调用才会被拦截。 这意味着自调用实际上是目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法标记有@Transactional,在运行时也不会导致实际事务。 此外,必须完全初始化代理以提供预期的行为,因此不能在初始化代码(即@PostConstruct)中依赖此功能。

为了加强关于从未注释的方法调用已注释方法的警告:即使对于已注释的方法,它也不会启动事务。另请参阅mok的答案。 - kaicarno

48

@Transactional 应用于服务上的每个方法。这是一个快捷方式。通常,如果您知道所有方法都将访问存储库层,则可以在服务类上设置 @Transactional(readOnly = true)。然后,您可以使用 @Transactional 在执行模型更改的方法上覆盖行为。1) 和 2) 之间的性能问题尚未知晓。


如果我在一个类上注释了 @Transactional(value="notPrimaryTransactionManager"),然后在一个成员方法上注释了 @Transactional(readOnly=true),那么这个方法会使用 "notPrimaryTransactionManager" 还是会使用 Spring 提供的默认事务管理器? - Anjil Dhamala

28

假设您有以下类:

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

  public Foo getFoo(String fooName) {
    // do something
  }

  // these settings have precedence for this method
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

类级别上的@Transactional注解将应用于类中的每个方法。

但是,当一个方法带有@Transactional注解时(比如:updateFoo(Foo foo)),此设置将优先于在类级别上定义的事务设置。

更多信息:


8
仅当外部调用updateFoo时才会如此。如果getFoo调用updateFoo(如原帖所问),则整个事务将为只读。 - David Newcomb

21
引用自此处的话:

Spring团队的建议是,只有具体类才能使用@Transactional注释,而不是接口。

由于该机制基于代理,因此只会拦截通过代理传入的“外部”方法调用。这意味着,“自我调用”,即目标对象内的一个方法调用目标对象的另一个方法,在运行时即使被标记为@Transactional,也不会导致实际事务发生!


9
这并不说明使用 @Transactional 注解的方法和使用 @Transactional 注解的类之间的区别。 - Konstantin Yovkov
1
@kocko> 我以为原帖会跟随链接,但我刚刚更新了我的答案,请看一下。 - Mohsen Kamrani
1
@mok,我曾经遇到过“自调用不会创建事务”的问题,这个答案让我想起了以前学习的内容。谢谢! - asgs
这并没有解释OP所问的区别,而只是强调了使用@Transactional注释方法时需要注意的地方。链接的文档已更新。为了后人的利益,最新的文档说:Spring团队建议您只使用@Transactional注释具体类(和具体类的方法),而不是注释接口。 - djpanda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接