15

I have the following code:

@Service
public class ItemService {
    ...

    public void addItems(@Nonnull DocumentDTO dto) throws Exception {
        // some code that takes some time to process
        ...

        addItems(dto.getDocId(), items);
    }

    @Transactional
    public void addItems(long docId, @Nonnull List<Item> items) {
        itemDao.addItems(docId, items);
    }
}

The first method is not @Transactional and it calls the second one with @Transactional. SonarLint tool states that "Methods should not call same-class methods with incompatible "@Transactional" values" (https://rules.sonarsource.com/java/RSPEC-2229)

But this code works correctly in Spring 4.3.20. Is this rule actual for Spring 4.3.20?

P.S. Interesting, if I make the second method as package-private, the SonarLint warning disappears... Why?

Andronicus
  • 25,419
  • 17
  • 47
  • 88
serg kunz
  • 505
  • 1
  • 4
  • 9
  • Due to springs proxy based AOP implementation (which is in turn used by the @Transactional annotation), transactions will only be active if the method is called from outside the class (via the proxy). Internal method calls won't run in a transaction. – Henry Feb 07 '19 at 05:56
  • To answer question "why it is work" you need to show `itemDao.addItems` code. – talex Feb 07 '19 at 05:58
  • When you say it work, what you are seeing is successful write of the data. What will not work is the rollback in case of a failure. It is actually doing a database wriite with auto_commit. – vavasthi Feb 07 '19 at 06:01

2 Answers2

18

But this code works correctly in Spring 4.3.20. Is this rule actual for Spring 4.3.20?

Yes. SonarLint is correct. Self-invocation cannot make @Transactional to take effect. It does not change even in Spring 5. That is how Spring AOP works (refer to docs). Your codes works most probably because you start another transaction inside itemDao (May be you have another @Transactional marked on ItemDao#addItems()).

if I make the second method as package-private, the SonarLint warning disappears... Why?

Don't know why. Maybe it is a bug. As mentioned in this rule , it should give you warning when mark @Transactional in private method.

Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • 2
    Sorry for digging out this question but I don't understand why is this a problem with "blocker" severity? In my opinion it just should warn that if you expect @Transactional to work when you call second method, you're wrong. But it states that it "will result in runtime exceptions because Spring only "sees" the caller and makes no provisions for properly invoking the callee". Which is totally incorrect. At least I've never faced any runtime exceptions in such cases. You just work with transaction created in first method (if there is one). Could you elaborate on this? – mykola Sep 22 '21 at 15:41
6

It might work properly, when there is no need for transaction. @Transactional only works on methods invoked on proxies created by spring. It means, that when you create a @Service or other bean, method called from the outside will be transactional. If invoked from within bean, nothing will happen, as it doesn't pass through proxy object.

For more details refer to this question.

Andronicus
  • 25,419
  • 17
  • 47
  • 88