Read only data via Spring + Hibernate - java

Noticed that if I want to read some data and if I do not have a transaction context I will not be able to do so because
org.hibernate.HibernateException: No Session found for current thread
For reading data , is not required a transaction normally.
So in order for Spring to manage the session it needs to have a transaction even for read only operations like selects... ?
Is that not an overhead ?
PS.I do not want to open and close session manually...
Thanks a lot.

#Transactional tells spring to open and close a session, in addition to instructing it to start and commit a transaction. This is not very straightforward, but that's how it works. So if you don't have #Transactional, no session gets opened. Here are your options:
use #Transactional(readOnly=true) - the purpose is to have a read-only transaction. I recommend that one
use JPA EntityManager injected with #PersistenceContext. It will open a new underlying session for each invocation. Not that good option. But you should consider using EntityManager with a readOnly=true transaction
Use an additional aspect/interceptor/filter to open and close session. That would be hard, and you may end up confused by the spring implementation of hibernate's current session concept.

Related

Open Session In View (OSIV) and Hibernate Session flush

Following is a hypothetical situation on Spring 3.x and Hibernate3.x
I have a service layer in spring which invokes 3 DAOs to construct a model.
The DAOs are transactional(#Transactional) and have lazy loaded hibernate collections.
The service method causes a few updates ,along with the fetch of data.
A typical DAO method will be as follows -
public O create(I entity) throws GenericException {
getOrCreateSession().save(entity);
return (O)entity;
}
I have the following questions around OSIV -
1.How many times is this session flushed(database update) in the default AUTO mode?
2.Can OSIV be made to extend the session beyond a single request (to a conversation)?
The AUTO flush mode will execute the pending DML statements when:
the current transaction is committed
when a query might target an entity table, that's current enqueued for flushing
Spring Webflow has support for long conversations.

Difference between HibernateTransactionManager and OpenSessionInViewFilter

From the docs - http://docs.spring.io/spring-framework/docs/2.0.x/api/org/springframework/orm/hibernate/HibernateTransactionManager.html
HibernateTransactionManager - Binds a Hibernate Session from the specified factory to the thread, potentially allowing for one thread-bound Session per factory
OpenSessionInViewFilter - This filter makes Hibernate Sessions available via the current thread, which will be autodetected by transaction managers.
What is the difference between both of them and at what scenarios should they be used ?
OpenSessionInViewFilter
Now when you are using OpenSessionInViewFilter, by default the session's flush mode is set to NEVER. So when you try to save something in your action using hibenate and commit it, it wont be reflected in your database. To solve this you need to flush the session in your action class or extend OpenSessionInViewFilter and override closeSession(Session session, SessionFactory sessionFactory).
Now it is also possible that you are maintaining a single transaction for per request. In your action you edit the attributes of a object and update it using session.update(object). But it is not yet commited as some other processing is remaining. At the same time, some other request is invoking a action which tries to retrieve the object which you were updating. Since the object is not yet commited the other request will get the old object. To solve this you need to begin a transaction before you load object and commit the transaction after you update the object. So that as soon as the object is saved/updated it is commited. With this there can be many transaction in single user request but only one session.
The OpenSessionInView pattern only guarantees that the session is open during one single thread execution.
When the page has been rendered and has been returned to the browser, the session gets closed by the filter.
So subsequent requests (e.g. navigation request) require another new session which will be opened by the OpenSessionInViewFilter. But as the "old" person object is not connected to the "new" session, it is considered as disconnected object which's references cannot be loaded lazily.

Best practice for querying inside event listeners in Hibernate

We have a FlushEventListener to do audit functionality. While updating some entity, hibernate will callback our audit code just before flushing. The audit code needs to query the database.
If we try to do it in the same session apparently we mess up the session's state: we get a NullPointerException from inside hibernate, at some point when it's validating naturalIds inside a class named NaturalIdXrefDelegate.
We currently solved it by opening a new session for the audit query. The problem with this is we're losing the benefit of getCurrentSession (a session for the whole request, managed by hibernate). This way we're going back to opening one session per query.
Is there an elegant solution for this or we basically need to re-implement getCurrentSession to manage our own session #2 in the request?
You don't have to open new session. It's enough to temporarily disable flush.
Session session = entityManager.unwrap(Session.class);
session.setHibernateFlushMode(FlushMode.MANUAL);
// do your db stuff
session.setHibernateFlushMode(FlushMode.AUTO);
It's actually a lot faster than
session.getSessionFactory().openSession()
Which works to btw.

Session Management with Java Hibernate

I have a Hibernate-based platform, built from stateless servlets (one is used to register a user and the rest to query the db).
I'm using Hibernate's sessions as follows:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
if ((null == session) || (session.isOpen() == false)) {
session = HibernateUtil.getSessionFactory().openSession();
}
Currently I do not close the session at the end of the servlet in order to avoid openSession() call (trying to use opened sessions if possible).
What's the best practice ? when am I supposed to close these sessions ?
Can you please give an example ?
Thanks in advance !
The best practice is in most cases session-per-request. That is, open a session in the beginning of handling a request, and close it in the end. You can do that in a Servlet Filter, for example.
Having one session for the entire application is bad, because it will accumulate a lot of entities in its 1st level cache, which is a memory leak. It may also produce undeterministic results when multiple clients use it at the same time.
Your code, however, is not using one session for the entire application - it is using the "current session" concept, which opens a session and stores it in a context (a ThreadLocal for example). But if you don't close it, it will stay there forever. Plus, it will cause the same problems as described above, because threads are reused in a web application, and a new request will get an old, unclosed session at some point.
Its always better to open a new session for every request, and close the session once the request is processed. Like
Session session = HibernateUtil.getSessionFactory().openSession();
instead of
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
If we use the getCurrentSession() method , tansaction.commit() / rollback() closes the connection.
The best is to manage a hibernate session is to open a new session for every request.
It all depends on how you obtain the session.
if you use sessionFactory.getCurrentSession(), you'll obtain a
"current session" which is bound to the lifecycle of the transaction
and will be automatically flushed and closed when the transaction
ends (commit or rollback)
if you decide to use sessionFactory.openSession(), you'll have to
manage the session yourself and to flush and close it "manually".
if (!session.isOpen()) {
session = session.getSessionFactory().openSession();
session.beginTransaction();
}
I better recommend you to use spring framework. Cause in spring you can use #Transactional at method level and session will be automatically created and closed by transaction manager (unless you are using any open session in view interceptor) using AOP which is internally handled by framework.
#Autowired
EntityManager em;
#Transactinal
void save(User user){
em.persist(user);
}
thats all.spring is fun :D

Bypassing the Hibernate cache in a transaction

I have a strange use case of Hibernate where in a call farther up a large stack needs an unmodified copy of an object that is part of a hibernate transaction. however, every time I ask Hibernate for a copy of the object, it's returning the version that's already been modified / is part of the transaction.
Is there a way for me to force Hibernate to return the db copy / uncached version of object in question?
Open a new Session and get the copy of the object from the new Session.
Session#refresh(Object object);
Re-read the state of the given instance from the underlying database.
or you can call session.evict(obj) (removes it from the session cache) and load() it afresh.
Note that if you are using EntityManager instead of Session, you won't have the evict() method. You can obtain the Session this way:
Session session = (Session) entityManager.getDelegate();
If you do not care about hibernate caching, use a stateless session to execute your query. Google Hibernate stateless session to get the details.

Categories