In spring framework project > In a single Request I called foo() method which is #Transactional and next called bar() method which is also #Transactional.
My question is do the entities loaded in foo() are still available in the persistance context. My point is, the Transactional is over but persistance context is exist science the hibernate session is still alive science the request is not yet completed.
Let's say we have bean FooBean:
#Component
public class FooBean {
#Transactional
public void foo(){}
#Transactional
public void bar(){}
}
And both methods are called from method baz of bean BazBean:
#Component
public class BazBean {
public void baz(){
foo();
bar();
}
}
If method baz already executed in transaction, i.e. it marked as transactional:
#Transactional
public void baz(){
Or BazBean marked as transactional:
#Component
#Transactional
public class BazBean {
Or as transactional marked any method up on calling stack.
Than foo and bar will be executed in one transaction.
Otherwise foo and bar will be executed in different transactions.
Please note, that it all was described for #Transactional without parameters. See paramteter propagation for #Transactional: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html
Related
Say I have the following spring beans in a spring boot app. My intent is to make createFoo() transactional so:
When barService.bar() throws exceptions, the persist gets rolled back.
When the persist throws exception, the exception propagates up to the caller "immediately" so barService.bar() is NOT called.
#Service
public class FooService
{
#Autowired
private FooRepository fooRepository;
#Autowired
private BarService barService;
#Transactional
public void createFoo(Foo foo) {
fooRepository.save(foo);
// expect to not execute when the above line throws exceptions
barService.bar();
}
}
#Service
public class BarService {
public void bar() {
}
}
So far the 1st requirement works, however the 2nd doesn't. When the persist throws exception, barService.bar() is AlWAYS called.
If I remove #Transactional, 2nd requirement works, the 1st doesn't.
I also tried all Propagation types, none of them work as I expected. For example, if I use #Transactional(MANDATORY), I get the following error:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
Without #Transactional, each call to a repo method is a standalone transaction and it will be flushed immediately. That's why your 2nd requirement worked without #Transactional.
When you add #Transactional, the whole createFoo() becomes one single unit of transaction. Hence, the changes you make when calling save() will only be flushed when createFoo() completes its execution. That's why your 1st requirement worked with #Transactional.
To achieve what you want, keep #Transactional and call saveAndFlush() instead of save().
I have two questions.
If I have a method:
#Transactional
public method1(){
method2()
}
public method2(){
dao.save()
}
If there is an exception in method2(), will there be a rollback?
Another question:
If I have a method:
#Transactional
public method1(){
method2()
}
private void method2(){
dao.save()
}
If there is an exception in method2(), will there be a rollback?
Yes, there will be a rollback.
The private methods will run within the same transaction. You should be aware that you can't have a #Transactional private method. It will not work without raising any error. This behavior is explained in Spring Docs:
Due to the proxy-based nature of Spring’s AOP framework, calls within
the target object are by definition not intercepted. For JDK proxies,
only public interface method calls on the proxy can be intercepted.
Yes to both. Transactional method means there must be no error during the entire runtime of the method.
If there is an error on one of the methods you are calling from within, these errors will be propagated and make the transaction fail and rollback.
I'm recently playing a little with Spring/JPA2 to understand better how it works. During my experiments I found some strange behaviour. The question is:
Why the following code works well (confirmed record added in db):
#Repository
public class UserDAO {
#PersistenceContext
EntityManager em;
#Transactional
public void add(User user) {
doAdd(user);
}
public void doAdd(User user) {
em.persist(user);
}
}
But the following (#Transactional annotation moved to inner method):
#Repository
public class UserDAO {
#PersistenceContext
EntityManager em;
public void add(User user) {
doAdd(user);
}
#Transactional
public void doAdd(User user) {
em.persist(user);
}
}
Throws exception:
javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:273)
at com.sun.proxy.$Proxy556.persist(Unknown Source)
at com.example.UserDAO.doAdd(UserDAO.java:24)
...
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.
Source
The #Transactional annotation support works by wrapping the actual DAO instance in a Proxy, which intercepts the method calls and starts/commits the transaction. In the second example the actual UserDAO instance is calling the doSave method and therefore there is no Proxy to intercept the method call.
I know that when you put say #Transactional on a class each method inside will be executed as part of one single transaction if you call them one after the other in sequence because the default spring transactional behaviour is PROPAGATION_REQUIRED. See docs http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#tx-propagation . So assume I have a class like:
#Service
#Transactional
public class ActorServiceImpl implements ActorService
{
public void method1(){}
public void method2(){}
}
However, I want to know what happens if method1 throws an exception and the transaction has to be rolled back then I guess method2 will never be executed because the transaction has already committed due to the exception in method1?
I am trying to update a resource inside an asynchronous thread that repeats its task until cancelled. I am using Future to store the thread in a hashmap so it can be cancelled at a later time. The thread forks as expected, and the object instance within the Async method changes its values fine, but when I try to commit them, the changes are not reflected in the DB, and I cannot see any error messages on the console. I've tried many different combinations of #Async and #Transactional but still haven't gotten it. What am I doing wrong? Thanks in advance for any help!
RoomService.java
#Service
#Transactional
public class RoomService {
#Inject private FooService fooService;
...
public Future<String> startRoom(Room room) {
Future<String> future = fooService.startFoo(room.getId());
map.put(room.getId(), future);
return future;
}
FooService.java
#Service
#Transactional
public class FooService {
...
public void save(Foo foo) {
getSession().saveOrUpdate(foo);
}
#Async
public Future<String> startFoo(int id) {
Foo foo = getFooById(id);
try {
while (true) {
foo.setName("Peter");
save(foo);
Thread.sleep(10000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return new AsyncResult<String>("done");
}
Having the #Async method be part of a #Transactional bean means Spring intercepts your method twice: once for #Transactional to wrap the method in a Transaction and once for #Async to execute the method in a separate Thread.
Because of the way proxying works, once you are inside a method of FooService, invoking
another method is done on the actual object, not on the proxy which has all the #Transactional behavior. As such, no new transaction is created/committed/rolled back. The actual Transaction boundary is the #Async method itself. But since your method executes an infinite loop, that commit will never happen.
I suggest you make your #Async method in a different class for which you create a bean and inject the FooService. This way, the #Transactional behavior is wrapping the actual call to
fooService.save(foo); // fooService is actually a proxy
and is therefore committed on each invocation.