117

Case1

@Transactional
public class UserServiceImpl implements UserService {

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

        }
    }
    public void method2(){

    }
}

Case2

public class UserServiceImpl implements UserService {

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

        }
    }
    @Transactional
    public void method2(){

    }
}

In case1 if any exception occurs it rollback is working, but in case 2 it's not working. Is there any performance issues if I follow the case1?

Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147
Anil Kumar
  • 1,431
  • 3
  • 13
  • 14

4 Answers4

140

In case 1 @Transactional is applied to every public individual method. Private and Protected methods are Ignored by Spring.

Spring applies the class-level annotation to all public methods of this class that we did not annotate with @Transactional. However, if we put the annotation on a private or protected method, Spring will ignore it without an error.

In case 2 @Transactional is only applied to method2(), not on method1()

Case 1: - Invoking method1() -> a transaction is started. When method1() calls method2() no new transaction is started, because there is already one

Case 2: - Invoking method1() -> no transaction is started. When method1() calls method2() NO new transaction is started. This is because @Transactional does not work when calling a method from within the same class. It would work if you would call method2() from another class.

From the spring reference manual:

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. @PostConstruct.

Harsh Mishra
  • 1,975
  • 12
  • 15
niekname
  • 2,528
  • 1
  • 13
  • 27
  • To double up on the warning of invoking an annotated method from a non-annotated method: it does NOT start a transaction, even for the annotated method. See also mok's answer. – kaicarno May 23 '19 at 19:51
48

@Transactionalon a class applies to each method on the service. It is a shortcut. Typically, you can set @Transactional(readOnly = true) on a service class, if you know that all methods will access the repository layer. You can then override the behavior with @Transactional on methods performing changes in your model. Performance issues between 1) and 2) are not known.

James Winsdor
  • 53
  • 1
  • 7
jeromerg
  • 2,997
  • 2
  • 26
  • 36
  • If I annotate a class Transactional(value="notPrimaryTransactionManager") and then annotate a member method with Transactional(readOnly=true), will the method use "notPrimaryTransactionManager" or will it use spring provided default transaction manager? – Anjil Dhamala Apr 09 '20 at 21:32
28

Suppose you have the following class:

@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
  }
}

The @Transactional annotation on the class level will be applied to every method in the class.

However, when a method is annotated with @Transactional (like, updateFoo(Foo foo)) this will take precedence over the transactional settings defined at the class level.

More info:

Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147
  • 8
    But only if updateFoo is called externally. If getFoo calls updateFoo (like the OP asked) then the complete transaction will be readonly. – David Newcomb Mar 04 '19 at 11:37
21

Quoting from here

The Spring team's recommendation is that you only annotate concrete classes with the @Transactional annotation, as opposed to annotating interfaces.

Since this mechanism is based on proxies, only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with @Transactional!

Mohsen Kamrani
  • 7,177
  • 5
  • 42
  • 66
  • 9
    This doesn't point the difference between `@Transactional` annotated method and `@Transactional` annotated class. – Konstantin Yovkov Apr 17 '14 at 12:22
  • 1
    @kocko> I thought the OP will follow the link, nevertheless I've just updated my answer, please take a look. – Mohsen Kamrani Apr 17 '14 at 12:25
  • 1
    @mok I was facing the "self-invocation won't create a transaction" problem and this answer made me remind the learning I had some time back. Thank you! – asgs Dec 31 '18 at 07:22
  • This does not explain the difference the OP was asking, but only highlights a gotcha with annotating the methods with ```@Transactional```. The linked documentation is now updated. For posterity's sake, the latest documentation says: _The Spring team recommends that you annotate only concrete classes **(and methods of concrete classes)** with the @Transactional annotation, as opposed to annotating interfaces._ – djpanda May 05 '20 at 22:36