Spring managed transactions, EclipseLink JPA, custom isolation level - java

I suspect this one is embarrassing an I am doing it wrong in a terrible way, but please bear with me.
I have a Spring application with Spring-managed transactions.
It uses EclipseLink JPA.
I have a method which does a findByNativeQuery() followed by a merge(). I need this to happen in a real SERIAL transaction isolation level.
I tried adding
#Transactional(isolation=Isolation.SERIALIZABLE)
This doesn't work because org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect#beginTransaction
does not support any transaction isolation level but the default.
So then I tried getting to UnitOfWork internals of ElcipseLink and starting/comitting my own transactions, but then I get an error:
"java.lang.IllegalStateException : Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
Which of course makes sense... but what do I do??

I've given a try to this, but I am not completely sure of the solution. I've taken the code from this blog and adapted it for EclipseLink. Here's the code:
package com.byteslounge.spring.tx.dialect;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
public class CustomEclipseLinkJpaDialect extends EclipseLinkJpaDialect {
private static final long serialVersionUID = 1L;
private boolean lazyDatabaseTransaction = false;
#Override
public void setLazyDatabaseTransaction(boolean lazyDatabaseTransaction) {
this.lazyDatabaseTransaction = lazyDatabaseTransaction;
}
#Override
public Object beginTransaction(final EntityManager entityManager,
final TransactionDefinition definition)
throws PersistenceException, SQLException, TransactionException {
UnitOfWork uow = (UnitOfWork) getSession(entityManager);
uow.getLogin().setTransactionIsolation(definition.getIsolationLevel());
entityManager.getTransaction().begin();
if (!definition.isReadOnly() && !lazyDatabaseTransaction) {
uow.beginEarlyTransaction();
}
return null;
}
}
I'm seeing the SERIALIZABLE isolation being logged when the transaction is initiated, but this needs to be tested properly to confirm it works.

Support for custom isolation level was added in Spring 4.1.2 to EclipseLinkJpaDialect

You can refer
"To achieve serializable transaction isolation with EclipseLink, we recommend that you use an isolated client session as follows:
Configure the database transaction isolation as serializable.
Configure objects as isolated (see Configuring Cache Isolation at the Project Level or Configuring Cache Isolation at the Descriptor Level).
Use the UnitOfWork method beginTransactionEarly (see Unit of Work Method beginTransactionEarly).
If you are only concerned about the write aspect of serializable, optimistic locking is sufficient."
at http://docs.oracle.com/middleware/1212/toplink/OTLCG/cache.htm or go through
http://docs.oracle.com/middleware/1212/toplink/OTLCG/cache.htm
if any of isolation level meets your requirement

Related

How do I use multiple #Transactional annotated methods?

I have a Spring-boot project where I have a service bean with 2 #Transactional annotated methods.
These methods do read-only JPA (hibernated) actions to fetch data from an HSQL file database, using both JPA repositories and lazy loaded getters in entities.
I also have a cli bean that handles commands (Using PicoCLI). From one of these commands I try to call both #Transactional annotated methods, but I get the following error during execution of the second method:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
at <mypackage>.SomeImpl.getThings(SomeImpl.java:<linenr>)
...
If I mark the method that calls both #Transactional annotated methods with #Transactional itself, the code seems to work (due to there now only being 1 top level transaction I presume?).
I just want to find out why I cannot start multiple transactions in a single session or why the second transaction doesn't start a new session if there are none.
So my questions are:
Does this have to do with how hibernate starts a session, how transactions close sessions or anything related to the HSQL database?
Is adding an encompassing transaction the right way to fix the issue
or is this just fighting the symptom?
What would be the best way to be able to use multiple #Transactional annotated methods from one method?
EDIT: I want to make clear that I don't expose the entities outside of the transactional methods, so on the surface it looks to me like the 2 transactional methods should be working independently from one another.
EDIT2: for more clarification: the transactional methods need to be available in an api and the user of the api should be able to call multiple of these transactional methods, without needing to use transactional annotations and without getting the LazyInitializationException
Api:
public interface SomeApi {
List<String> getSomeList();
List<Something> getThings(String somethingGroupName);
}
Implementation:
public class SomeImpl implements SomeApi {
#Transactional
public List<String> getSomeList() {
return ...; //Do jpa stuff to get the list
}
#Transactional
public List<Something> getThings(String somethingGroupName) {
return ...; //Do other jpa stuff to get the result from the group name
}
}
Usage by 3rd party (who might not know what transactionality is):
public someMethod(String somethingGroupName) {
...
SomeApi someApi = ...; // Get an implementation of the api in some way
List<String> someList = someApi.someList();
if (someList.contains(somethingGroupName) {
System.out.println(someApi.getThings(somethingGroupName));
}
...
}
It seems that you are accessing some not initialized data from your entities after the transactions have ended. In that cases, the persistence provider may throw the lazyinitialization exception.
If you need to retrieve some information not eagerly loaded with the entities, you may use one of two strategies:
annotate the calling method also with #Transactional annotation, as you did: it does not start a new transaction for each call, but makes the opened transaction active until your calling method ends, avoiding the exception; or
make the called methods load eagerly the required fields USING the JOIN FETCH JPQL idiom.
Transaction boundaries requires some analysis of your scenario. Please, read this answer and search for better books or tutorials to master it. Probably only you will be able to define aptly your requirements.
I found that hibernate out of the box doesn't reopen a session and therefore doesn't enable lazy loading after the first transaction has ended, whether or not subsequent jpa statements are in a transaction or not. There is however a property in hibernate to enable this feature:
spring:
jpa:
properties:
hibernate.enable_lazy_load_no_trans: true
This will make sure that if there is no session, then a temp session will be created. I believe that it will also prevent a session from ending after a transaction, but I don't know this for sure.
Partial credit goes to the following answers from other StackOverflow questions:
http://stackoverflow.com/a/32046337/2877358
https://stackoverflow.com/a/11913404/2877358
WARNING: In hibernate 4.1.8 there is a bug that could lead to loss of data! Make sure that you are using 4.2.12, 4.3.5 or newer versions of hibernate. See: https://hibernate.atlassian.net/browse/HHH-7971.

Spring boot Persistence Context annotation

I am new in Spring Boot and trying to create a basic REST example in Spring boot. I am taking help from Spring Boot REST example website to create a basic example.
Most of the things are clear to me but I am stuck with one annotation which is being used to fetch the data from the database with the code as below
package com.springbootrest.repository;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import org.springframework.stereotype.Repository;
import com.springbootrest.model.BookDetails;
#Transactional
#Repository
public class BookDetailsRepoImpl implements BookDetailsRepo {
#PersistenceContext
private EntityManager entityManager;
public List<BookDetails> listBookDetails() {
return (List<BookDetails>) entityManager.createQuery("FROM BookDetails").getResultList();
}
}
I don't understand how #PersistenceContext is actually working - can anyone please explain?.
#PersistenceContext – We need to understand how we are able to connect with the database using just simple annotation #PersistenceContext and what it is.
Entities are managed by javax.persistence.EntityManager instance
using persistence context.
Each EntityManager instance is associated with a persistence
context.
Within the persistence context, the entity instances and their
lifecycle are managed.
Persistence context defines a scope under which particular entity
instances are created, persisted, and removed.
A persistence context is like a cache which contains a set of
persistent entities , So once the transaction is finished, all
persistent objects are detached from the EntityManager’s persistence
context and are no longer managed.
The #PersistenceContext annotation in your code is being used to indicate that the EntityManager must be automatically injected, in other words its lifecycle will be managed by the container running your application (which is a good thing). The other option would be having all required configurations provided by you (application managed) via different options, all of them cumbersome (config files or beans) and running the risk of tying your application to some environment-specific configuration (which is a bad thing).
#PersistenceContext is JPA standard annotation which gives you better control of which persistence context you are Injecting.
My answer comes after quite a few years but here goes .
this annotation #PersistentContext works in conjunction with another bean defined in your application context:
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
The way the two work together is that the PersistenceAnnotationBeanPostProcessor will lookup the appropriate EntityManagerFactory to inject the entity manager where we have attributes annotated with #PersistenceContext
My understanding is based on the answers to this question: so question here
In Short or layman language, Its a space(just to say) where entities are managed using Entity Manager.

How to use Eclipselink #Multitenant in JSF/EJB?

The #Multitenant support in Eclipselink 2.3 looks really interesting, but I'm having a hard time understanding how to use it in a JSF or EJB which injects an EntityManager with #PersistenceContext. The EclipseLink docs are pretty clear that #PersistenceContext injection doesn't work in this case, but you could inject an EntityManagerFactory via #PersistenceUunit instead.
Still, what I'm not seeing is how to manage the lifecycle of an EntityManager you might create via injected EntityManagerFactory.createEntityManager() - in particular, when to close the resulting EntityManager, and how to participate in transactions.
Has anyone gotten this to work? Or am I missing something obvious?
See also: http://wiki.eclipse.org/EclipseLink/Examples/JPA/Multitenant
UPDATE
I had some success with #PersistenceContext (EntityManager) injection and then passing parameters to EclipseLink via session listener. I'm not 100% sure this is the right answer and would appreciate confirmation that it isn't creating a non-obvious race condition or thread-safety issue.
For example:
public static class TenantListener extends SessionEventAdapter {
#Override
public void postAcquireClientSession(SessionEvent event) {
long tenantId = **business logic**;
event.getSession().setProperty("eclipselink.tenant-id", tenantId);
}
}
Using events is fine.
You could also inject the EntityManager and set the property, or inject the EntityManagerFactory and use joinTransaction() to join the active JTA transaction.

hibernate jpa: Session is closed!

Application is based on Spring 2.5.5 and hibernate 3.2.0 GA.
I have the following method in my DAO that gets MessageEntities attached to the specified User:
public MessageEntity findByUserId(int userId) {
List<MessageEntity> result = (List<MessageEntity>) em.createNamedQuery(MessageEntity.Q_BY_USER_ID).setParameter("userId", userId).getResultList();
if (!result.isEmpty()) {
return result.get(0);
} else {
return null;
}
}
I need to call this method from my integration test to check whether system's behaviour is valid. As long as this method is not transactional, all I get is org.hibernate.SessionException: Session is closed!. The easiest way to avoid this is to mark findByUserId method with #Transactional(readOnly = true). But as I understand, transaction management should be the duty of service tier to avoid unnecessary transactions creation. So, my question is: how can I properly get away from SessionException?
You need to perform all your database actions within a transaction scope. As you identified its usually considered good design to let the service layer of your database model deal with transactions. The only constraint then becomes that you must invoke your service model to get within the transaction scope, which might be undesirable during test.
I would recommend to make use of the testing fascilites provided by spring. See 9.3.2.3 Transaction management
You could also manually create a transaction before testing your method, e.g., by
Session sess = factory.openSession();
Transaction tx = null;
// try catch
tx = sess.beginTransaction();
findByUserId(userId);
tx.commit();
tx.rollBack();
Put the following annotations on the top of your test class.
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
Also I wouldn't worry about putting additional #Transactional in DAOs.
Spring usually checks to see if you are already in a transaction (with in the same thread) before it creates another.
"But as I understand, transaction
management should be the duty of
service tier to avoid unnecessary
transactions creation."
This is more of a design choice (Spring Roo for example violates this)
You can use this annotation on your controller method:
#Transactional(readOnly = true)

HibernateDaoSupport , transaction is not rolled back

I'm playing around with Spring + Hibernate and some "manual" transaction management with PostgreSQL
I'd like to try this out and understand how this works before moving to aop based transaction management.
#Repository
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
#Override
public void saveUser(User u) {
Transaction tx = getSession().beginTransaction();
getHibernateTemplate().saveOrUpdate(u);
tx.rollback();
}
}
Calling saveUser here, I'd assume that saving a new User will be rolled back.
However, moving to a psql command line, the user is saved in the table.
Why isn't this rolled back, What do I have to configure to do transactions this way ?
Edit; a bit more debugging seems to indicate getHibernateTemplate() uses a different session than what getSession() returns (?)
Changing the code to
Transaction tx = getSession().beginTransaction();
getSession().persist(u);
tx.rollback();
and the transaction does get rolled back. But I still don't get why the hibernateTemplate would use/create a new session..
A couple of possibilities spring to mind (no pun intended):
a) Your JDBC driver defaults to autocommit=true and is somehow ignoring the beginTransaction() and rollback() calls;
b) If you're using Spring 3, I believe that SessionFactory.getSession() returns the Hibernate Session object wrapped by a Spring proxy. The Spring proxy is set up on the Session in part to handle transaction management, and maybe it's possible that it is interfering with your manual transaction calls?
While you can certainly use AOP-scoped proxies for transaction management, why not use the #Transactional(readOnly=false|true) annotation on your service layer methods? In your Spring config file for your service layer methods, all you need to do to make this work is to add
<tx:annotation-driven />
See chapters 10 and 13 of the Spring Reference Documentation on Transaction Management and ORM Data Access, respectively:
http://static.springsource.org/spring/docs/3.0.x/reference/index.html
Finally, if you're using Spring 3, you can eliminate references to the Spring Framework in your code by injecting the Spring-proxied SessionFactory bean into your DAO code - no more need to use HibernateDaoSupport. Just inject the SessionFactory, get the current Session, and use Hibernate according to the Hibernate examples. (You can combine both HibernateDaoSupport and plain SessionFactory-based Hibernate code in the same application, if required.)
If you see the JavaDoc for HibernateDaoSupport.getSession() it says it will obtain a new session or give you the one that is used by the existing transaction. In your case there isn't a transaction listed with HibernateDaoSupport already.
So if you use getHibernateTemplate().getSession() instead of just getSession(), you should get the session that is used by HibernateTemplate and then the above should work.
Please let me know how it goes.
EDIT:
I agree its protected...my bad. So the other option then is to keep the session thread bound which is usually the best practice in a web application. If HibernateDaoSupport is going to find a thread bound session then it will not create a new one and use the same one. That should let you do rollbacks.

Categories

Resources