How to call #Transactional method from one service from other #Transactional method from other service - spring

I have:
1) Service:
#Service("scanner")
#Transactional
public class Scanner
{
#Inject
AnalyzerService analyzerService;
#Transactional
private void scan() {
analyzerService.analyze();
}
}
2) Service:
#Service
public class AnalyzerService
{
#Inject
AnalyzerDao analyzerDao;
#Transactional
public void analyze() {
List<AnalyzerResult> items;
// code filling items list removed;
save(items);
}
#Transactional
private void save(List<SomeType> items) {
analyzerDao.save(items); // <--- Why after call save items are not saved in DB?
}
}
3) Dao:
#Repository
public class AnalyzerDao extends GenericDaoImpl<AnalyzerResult>
{
//all needed methods for find, edit, delete and save which works fine in other cases.
}
Question:
Why after call analzyerDao.save(items) DB is still empty? Is it problem with transaction some how?
When I invoke flush() method and getSession().getTransaction().commit() just after line analyzerDao.save(items) then records appearing in DB but exception is thrown:
Caused by: org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:660)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy44.execute(Unknown Source)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
... 1 more
Caused by: org.hibernate.TransactionException: Transaction not successfully started
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:127)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
... 9 more
How should implementation be done to work 'save' method fine?
What should I do to save items just in line analyzerDao.save(items) and not only after first transaction will be finished?

What you need is a new transaction just to save what you need to save. You can achieve this by configuring the propagation of #Transactional annotation to REQUIRES_NEW.
Unfortunately your case is a bit tricky, because you are invoking a method within this context when you do save(items);, this means the transaction interceptor will not intercept such invocation, therefore you have the possibility to inject the service to a field hold by itself and invoke it on the injected service instead of this forcing the invocation to that method be intercepted by the transaction interceptor, please try the following implementation:
#Service
public class DefaultAnalyzerService implements AnalyzerService {
#Inject
AnalyzerDao analyzerDao;
#Inject
AnalyzerService analyserService;
#Transactional
#Override
public void analyze() {
List<AnalyzerResult> items;
// code filling items list removed;
analyserService.save(items);
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public void save(List<SomeType> items) {
analyzerDao.save(items); // <--- Why after call save items are not saved in DB?
}
}
Another thing that changed was the visibility of save(List<SomeType> items), that is public now on in order to be intercepted by transaction interceptor and an interface was extracted. This is needed due to limitations with spring, but you can use AspectJ to handle such interceptor, therefore please take a look here.

Data won't appear in the database until the transaction is committed. For #Transactional methods, the transaction is committed by Spring after returning from the method.
By the way, #Transactional on private methods has no effect, so Scanner.scan() is not transactional at all.

Related

I want nesting of transaction in spring boot using #Transactional annotation

Currently if exception occurs in method C, code is not rolling back database changes from method B. My expectation is method A should manage transaction in such a way that if exception occurs in method C, code should rollback changes done in method B.
I am using spring boot, maven project.
class SomeClassA{
#Autowired
SomeClassB someClassB;
#Autowired
SomeClassC someClassC;
#Transactional
public A(){
try{
//This method works fine with some database operations.
someClassB.B();
//In this method, exception occurrs.
someClassC.C();
}
catch(Exception e){
}
}
}
class SomeClassB{
#Transactional
public B(){
//some code with database operation
}
}
class SomeClassC{
#Transactional
public C(){
//some code with database operation
//some exception occurs here
}
}
Is it a Checked or Runtime exception ?
Because the default behaviour of #Transactional says:
Any RuntimeException triggers rollback, and any checked Exception does
not.

How to declare `#Transactional` in a Spring Boot service so exception is thrown "immediately"?

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().

Spring #Transactional placement

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.

EJB with spring : transaction issue in JPA flush

I have an issue with an injected EntityManager in my MessageDriven Bean that use spring bean as services (the bootstrap is done with the SpringBeanAutowiringInterceptor.
Here is the code :
#MessageDriven(name = "ProcessMDB")
#Interceptors(SpringBeanAutowiringInterceptor.class)
public class ProcessMDB implements MessageListener {
#Autowired
private ProcessService processService;
#Override
public void onMessage(Message message) {
try {
id = message.getLongProperty("ID");
processService.process(id);
} catch (Exception e) {
// Handle error.
}
}
The process service has a DAO where the EntityManager is injected with the annotation #PersistentContext...
The problem is that if a JPA error occurs in the processService, it may occur during the entityManager.flush() call... so the try catch block is gone and the //Handle error stuff is not done.
So I tried to add manually the flush.
#MessageDriven(name = "ProcessMDB")
#Interceptors(SpringBeanAutowiringInterceptor.class)
public class ProcessMDB implements MessageListener {
#Autowired
private ProcessService processService;
#PersistenceContext
private EntityManager em;
#Override
public void onMessage(Message message) {
try {
id = message.getLongProperty("ID");
processService.process(id);
em.flush();
} catch (Exception e) {
// Handle error.
}
}
But it seems that the flush has no effect.
I've try to add the em.flush in the underlying DAO (just after the persist for instance) and it works! The exception is well raised and the catch block is executed. But it doesn't work if I put the em.flush() at the MessageDrivenBean level.
I think that it's transaction manager problem... The entitymanager in spring beans is not in the same tx than the injected entity manager in my ejb.
If I make em.find in the onMessage() method, the fetched object holds old values (the one in the database), not values that are changed in the service method.
I've configured my database as followed :
<jee:jndi-lookup id="emf" jndi-name="persistence/PUnit" />
and my tx manager as followed :
<tx:annotation-driven/>
<tx:jta-transaction-manager />
What do I wrong?
Can someonee help me?
Thanks
Stéphane
You have injected EntityManager in ProcessMDB, but the object is being persisted in ProcessService.
Now, how can any operation in ProcessMDB can affect ProcessService, both will have probably their own individual EntityManager.
Scope of PersistenceContext is upto associated EntityManager. The object will be in the context of ProcessService & not in ProcessMDB, therefore calling flush on later will have no effect & this is expected behaviour.

What is the proper way to save a resource in an #Async task?

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.

Resources