2

I'm having the following code inside one of my services:

@Override
@Transactional
@RetryConcurrentOperation(exception = Exception.class, retries = 12)
public void test() {

Player player = this.playerRepository.findPlayerById(1L);
player.setFirstName("SomeName");
}

the retry mechanism i'm using is the one that was described here: http://josiahgore.blogspot.co.il/2011/02/using-spring-aop-to-retry-failed.html

problem is when i get an optimistic retry (2nd retry) i get an exception:

Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [xxx]

Interesting this is that the mechanism works when i'm removing the transactional annotation and within the non transactional function i'm calling a different transactional method:

// THIS WORKS: 
@Override
@RetryConcurrentOperation(exception = Exception.class, retries = 12)
public void test() {
 execute();

}   

@Override
@Transactional
public void execute() {
Player player = this.playerRepository.findPlayerById(1L);
player.setFirstName("SomeName");
}

Any ideas why this aspect retry mechanism is not succeeding when it's being invoked from a transactional function?

lior
  • 1,127
  • 3
  • 24
  • 43
  • in the end did @Transactional work in these cases? I thought there there two problems, one wat that transactional did not work for reentrant calls, and the second the blog post would not work for versioned entities, was this the answer you where looking for, or was it something else? – Angular University Feb 13 '14 at 17:02
  • Try this solution: https://stackoverflow.com/a/45543257/516167 – MariuszS Aug 07 '17 at 09:11

1 Answers1

0

When calling @Transactional execute() from non transactional test(), the @Transactional in execute() will NOT be applied. This is because it's a direct call from one method of the object directly to the other, which bypasses the transactional proxy.

See this answer for more details on how proxies work and why @Transactional does not work when calling the transactional function using this.

Have a look also at the Spring RetryTemplate, which is a spring-based solution for this problem.

Concerning the retry mechanism, it won't work for cases when a versioned entity (with a @Version column) is being used, which is the case given that StaleObjectStateException is being thrown.

The reason is that there was another thread updating the entity on the database, incrementing the version column.

The solution is to refresh() the entity (loading the latest version), reapply the modifications and try again. Retrying several times the same modification will only work in case of non versioned entities, and it's probably not what you want because the changes made by one thread are silently overwritten by another thread.

Community
  • 1
  • 1
Angular University
  • 42,341
  • 15
  • 74
  • 81
  • It seems that RetryTemplat is not vary generic. Every time i want retry I need to implement a special callback function. – lior Feb 09 '14 at 12:15
  • he callback allows to apply some recovery logic (for example write to another table, log anomalies in logging tables etc.). And where the updates/inserts being triggered in the database without @Transactional? – Angular University Feb 10 '14 at 16:40
  • I have added why I think @Transactional is not applied, it's a reetrant call that bypasses the proxy. Also with versioned entities (the case here) the strategy in the blog post does not work, see updated answer above – Angular University Feb 10 '14 at 16:56