EJB with spring : transaction issue in JPA flush - spring

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.

Related

Spring transactioninterceptor is invoked when no class or method annotated with transactional

I am working on a gigaspace xap application which uses spring under the hood. The jini transaction manager provided by gigaspaces does not support serializable.
I have a class which uses spring-batch to process a file. Below is how it is invoking the job
public class FileProcessor implements BasicFileProcessor {
#Value("${feeddownload.basedir}")
private String baseDir;
#Autowired
private JobLauncher jobLauncher;
#Autowired
private Job cmJob;
#Autowired
private MapJobRepositoryFactoryBean repositoryFactoryBean;
#Override
public void process(RiskRunCompletion riskRunCompletion, VersionedSliceName versionedSliceName, GigaSpace gigaSpace) {
Transaction tx = gigaSpace.getCurrentTransaction();
try {
//Adding current time to the parameter, to enable multiple times calling job with same parameters
long currentTimeInMillis = System.currentTimeMillis();
JobParameter currentTimeInMillinsParam = new JobParameter(currentTimeInMillis);
Map parameterMap = new LinkedHashMap();
addDirectoryParams(valuationSliceRun, parameterMap);
parameterMap.put(CURRENT_TIME, currentTimeInMillinsParam);
JobParameters paramMap = new JobParameters(parameterMap);
JobExecution cmExecution = launchJobWithParameters(paramMap);
for (Throwable t : cmExecution.getAllFailureExceptions()) {
throw new RuntimeException(t);
}
} catch (Exception e) {
throw new RuntimeException("Exception during batch job", e);
} finally {
repositoryFactoryBean.clear();
}
}
private JobExecution launchJobWithParameters(JobParameters paramMap) throws Exception {
return jobLauncher.run(cmJob, paramMap);
}
}
The process method is invoked from a different class as below
public class FileBasedProcessingEventListener implements ApplicationContextAware {
#Value("${feeddownload.basedir}")
private String baseDir;
#Autowired
private BasicFileProcessor cmProcessor;
#Autowired
private FileBasedProcessingExceptionHandler fileBasedProcessingExceptionHandler;
public void handle(FileBasedProcessingEvent fileBasedProcessingEvent, GigaSpace gigaSpace) throws IOException {
LOGGER.info("Processing file based processing event : " + fileBasedProcessingEvent);
createLockFiles(fileBasedProcessingEvent);
handleEvent(fileBasedProcessingEvent, gigaSpace);
}
private void handleEvent(FileBasedProcessingEvent fileBasedProcessingEvent, GigaSpace gigaSpace) {
Transaction tx = gigaSpace.getCurrentTransaction();
cmProcessor.process(fileBasedProcessingEvent.getRiskRunCompletion(), versionedSliceName, gigaSpace);
}
}
Handle method is called from the framework. Now i am not sure why i am getting the exception as below
Caused by: org.springframework.transaction.InvalidIsolationLevelException: Jini Transaction Manager does not support serializable isolation level
at org.openspaces.core.transaction.manager.AbstractJiniTransactionManager.applyIsolationLevel(AbstractJiniTransactionManager.java:271)
at org.openspaces.core.transaction.manager.AbstractJiniTransactionManager.doJiniBegin(AbstractJiniTransactionManager.java:251)
at org.openspaces.core.transaction.manager.AbstractJiniTransactionManager.doBegin(AbstractJiniTransactionManager.java:207)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:417)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:255)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
None of the classes are marked as transactional, i am not sure why TransactionInterceptor is being invoked when i have not marked any class or any method as transaction it should not be of any concern. I also used Transaction tx = gigaSpace.getCurrentTransaction(); to check that transaction is not active it comes as null only
i am confused when none of the classes are marked as transactional why is spring trying to invoke this method under transaction
Looks like Gigaspaces transaction manager is based upon Spring transaction management infrastructure as can be inferred from the documentation here - so if you are using Gigaspaces transaction API then you are using Spring transaction management. Also worth looking is any transaction manager configuration inside xml configuration files which would point the exact transaction manager class.
Its indeed seems that transactions are in use, check if you define a transaction manger in your pu.xml, in particular check jobLauncher initialization.

how to keep jpa session in the thread

Now I use JPA in my web application. And I have a class like this:
class A {
...
#OneToMany
private List<B> list;
...
}
When it is in a HTTP request, I can use a.getList() successful. But in a schedule thread, it throws the exception:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: A.list, could not initialize proxy - no Session
Can I keep the session in the schedule thread just like the http request thread?
Actually, when spring handle the http request, it start the transaction by the interceptor org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor or the filter org.springframework.orm.hibernate3.support.OpenSessionInViewFilter. So if we want to keep the session, we can start and commit the transaction by our self.
Here is the code:
public class BackendThread {
#Autowired
private PlatformTransactionManager platformTransactionManager;
#Override
public void onMessage(Message message, byte[] pattern) {
new TransactionTemplate(platformTransactionManager).execute(new TransactionCallback<Void>() {
#Override
public Void doInTransaction(TransactionStatus transactionStatus) {
...
your code here
...
return null;
}
});
}
}
TransactionTemplate is helpful if you use JPA. I didn't use it and created a session/transactions on my own in background thread, so that each task had its own environment:
Inject entity manager or get it from Spring context or pass it as reference:
#PersistenceContext
private EntityManager entityManager;
Then create a new entity manager, to avoid using a shared one:
EntityManager em = entityManager.getEntityManagerFactory().createEntityManager();
Now you can start transaction and use Spring DAO, Repository, JPA, etc
private void save(EntityManager em) {
try
{
em.getTransaction().begin();
<your database changes>
em.getTransaction().commit();
}
catch(Throwable th) {
em.getTransaction().rollback();
throw th;
}
}

How can I add an entity listener to a JPA (EclipseLink) entity without an active session?

I am trying to do the following inside a spring bean:
#PostConstruct
public void registerTorchEntityListeners()
{
Session session = entityManager.unwrap(Session.class);
for (EntityType<?> entity : entityManager.getMetamodel().getEntities())
{
if (entity.getJavaType().isAnnotationPresent(TorchEntityListeners.class))
{
TorchEntityListeners annotation = (TorchEntityListeners) entity.getJavaType().getAnnotation(TorchEntityListeners.class);
for (Class listenerClass : annotation.value())
{
Map<String, DescriptorEventListener> map = applicationContext.getBeansOfType(listenerClass);
for (DescriptorEventListener listenerBean : map.values())
{
session.getClassDescriptor(entity.getClass()).getEventManager().addListener(listenerBean);
}
}
}
}
}
The problem is I get the following exception because (I think) I am not in a transaction and therefore do not have a session available to grab the ClassDescriptor so that I can add a listener to a particular entity:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'torchEntityListenerConfigurer': Invocation of init method failed; nested exception is java.lang.IllegalStateException: No transactional EntityManager available
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:396)
Basically I am trying to do the EclipseLink equivalent of this: http://invariantproperties.com/2013/09/29/spring-injected-beans-in-jpa-entitylisteners/. I would prefer to annotate the entity with the listener rather than doing something like this: Injecting a Spring dependency into a JPA EntityListener.
Thoughts?
Of course I figure it out 30 minutes after I add a bounty :)
I finally got this to work by getting the entityManager from a wired in EntityManagerFactory instead of using: #PersistenceContext to inject it into the TorchEntityListenerConfigurer
Here is the working solution...and it works great!
Here is the config:
<bean id="approvalEntityListener" class="com.prometheus.torchlms.core.activity.approval.ApprovalEntityListener">
<property name="activityRepository" ref="activityRepository" />
<property name="notificationFactory" ref="notificationFactory" />
<property name="notificationService" ref="notificationService" />
</bean>
<bean id="springEntityListenerConfigurer" class="com.prometheus.torchlms.core.SpringEntityListenerConfigurer">
<constructor-arg ref="entityManagerFactory" />
</bean>
Here is where the magic happens (in case this is useful to someone):
public class SpringEntityListenerConfigurer implements ApplicationContextAware
{
private ApplicationContext applicationContext;
private EntityManagerFactory entityManagerFactory;
public SpringEntityListenerConfigurer(EntityManagerFactory entityManagerFactory)
{
this.entityManagerFactory = entityManagerFactory;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.applicationContext = applicationContext;
}
#PostConstruct
public void registerTorchEntityListeners()
{
//entityManager.
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
for (EntityType<?> entity : entityManagerFactory.getMetamodel().getEntities())
{
if (entity.getJavaType().isAnnotationPresent(SpringEntityListeners.class))
{
SpringEntityListeners annotation = (SpringEntityListeners) entity.getJavaType().getAnnotation(SpringEntityListeners.class);
for (Class listenerClass : annotation.value())
{
Map<String, DescriptorEventListener> map = applicationContext.getBeansOfType(listenerClass);
for (DescriptorEventListener listenerBean : map.values())
{
ClassDescriptor classDescriptor = session.getClassDescriptor(entity.getJavaType());
if (null != classDescriptor)
{
classDescriptor.getEventManager().addListener(listenerBean);
}
}
}
}
}
}
}
So now I can add my #SpringEntityListeners({ApprovalEntityListener.class}) to any entity I want with any listener that I want and those listeners can be spring beans!
In case it helps here's the annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface SpringEntityListeners
{
Class<?>[] value();
}
In fact you can do your registrations without getting an EntityManager, because you only use it to get a client session, and you only use the client session to register listeners with the server session without any direct interaction with the database nor changing any object.
The EntityManagerFactory is actually an EntityManagerFactoryImpl that can directly expose the ServerSession with unwrap. I had to dig through classes explicitly marked as INTERNAL, to find that, and the ServerSession (also marked as INTERNAL) explicitely states in his javadoc : All changes to objects and the database must be done through a unit of work acquired from the client session, this allows the changes to occur in a transactional object space and under a exclusive database connection.
But for your use case, I think it is correct to use it like that, using the server session only to get access to the Project that actually contains the class descriptors and is a public class in EclipseLink :
public void registerTorchEntityListeners()
{
// no entityManager here and Session is only used to get access to Project
Project proj = entityManagerFactory.unwrap(Session.class).getProject();
for (EntityType<?> entity : entityManagerFactory.getMetamodel().getEntities())
{
if (entity.getJavaType().isAnnotationPresent(SpringEntityListeners.class))
{
SpringEntityListeners annotation = (SpringEntityListeners) entity.getJavaType().getAnnotation(SpringEntityListeners.class);
for (Class listenerClass : annotation.value())
{
Map<String, DescriptorEventListener> map = applicationContext.getBeansOfType(listenerClass);
for (DescriptorEventListener listenerBean : map.values())
{
ClassDescriptor classDescriptor = proj.getClassDescriptor(entity.getJavaType());
if (null != classDescriptor)
{
classDescriptor.getEventManager().addListener(listenerBean);
}
}
}
}
}
}

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

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.

Spring #Transactional rollbackFor not working

I have a code like below
public abstract class AffltTransactionService implements IAffltTransactionService {
....
#Override
#Transactional
public void processTransactions(List<? extends AffltTransaction> transactions) {
for (AffltTransaction transaction : transactions) {
if (transaction != null) {
processTransaction(transaction);
}
}
}
private void processTransaction(AffltTransaction transaction) {
try {
processTransactionInternal(transaction);
} catch (Exception exception) {
affltTransactionError = new AffltTransactionError(null, null, "error", new Date());
saveAffltTransactionError(affltTransactionError);
log.error(exception.getLocalizedMessage());
}
}
#Transactional(readOnly=false, rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void processTransactionInternal(AffltTransaction transaction) {
processTransactionInternal throws ServiceUnAvailableException which extends RuntimeException
But the transaction is not getting rolled back despite having rollbackFor = Exception.class .
Can you please help.
#Transactional annotation won't have any effect if you are calling the method directly, since Spring creates proxies above annotated classes and the aspect-defined functionality is implemented by proxy. So, when you call the method from within your class it doesn't get through proxy, and hence no transcation is created and/or rolled back.
Take a look at the Spring reference for detailed explanation.
Since you invoke one method from another within the same bean, the Spring AOP doesn't use any advices in this case.
Only processTransactions is wrapped with TransactionInteceptor.
To make it worked you should configure:
<aop:aspectj-autoproxy expose-proxy="true"/>
But it isn't recommened, though.
More info here: http://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy
Use getCurrentSession instead of opensession
I know it was asked a long time ago but I faced with the same issue and for me was missing Spring configurantion annotation:
#EnableTransactionManagement
After write it on ApplicationConfiguration class it was solved. I hope it helps someone on future.
The method you use apply #Transactional should throws exception. Don't use try-catch instead.(I guess you use try-catch in somewhere in your processTransaction function).
Code should be like this:
#Transactional
public void processTransactions(List<? extends AffltTransaction> transactions) threws Exception{
for (AffltTransaction transaction : transactions) {
if (transaction != null) {
processTransaction(transaction);
}
}
}

Resources