In the standalone application(single threaded command line tool) I am developing ,I use Spring +Hibernate.
It has DAO and Service layers and for DAOs I use HibernateDAOSupport.
The collections in domain model are lazy-loading.
Since for lazy-loading I need to keep the Session opended,I open session at start of my application using:
HibernateTemplate tmpl;
SessionFactoryUtils.initDeferredClose(tmpl.getSessionFactory());
//do file reads,parse CSV , persist objects (normally takes along time)
//
//Finally
SessionFactoryUtils.processDeferredClose(tmpl.getSessionFactory());
But during execution ,I get the error:
Exception in thread "main"
org.springframework.orm.hibernate3.HibernateSystemException:
Illegal attempt to associate a
collection with two open sessions;
nested exception is
org.hibernate.HibernateException:
Illegal attempt to associate a
collection with two open sessions
Caused by:
org.hibernate.HibernateException:
Illegal attempt to associate a
collection with two open sessions at
org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:410)
at
org.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:43)
at
org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
at
org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61)
at
org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
at
org.hibernate.event.def.AbstractVisitor.process(AbstractVisitor.java:123)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:293)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:223)
at
org.hibernate.event.def.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:33)
at
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at
org.hibernate.impl.SessionImpl.fireUpdate(SessionImpl.java:564)
at
org.hibernate.impl.SessionImpl.update(SessionImpl.java:552)
How to fix this?
at
org.hibernate.impl.SessionImpl.update(SessionImpl.java:544)
at
org.springframework.orm.hibernate3.HibernateTemplate$14.doInHibernate(HibernateTemplate.java:657)
at
org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
at
org.springframework.orm.hibernate3.HibernateTemplate.update(HibernateTemplate.java:654)
at
org.springframework.orm.hibernate3.HibernateTemplate.update(HibernateTemplate.java:650)
at
com.bigg.nihonbare.common.dao.hibernate.CommonDAOImpl.updateFamily(CommonDAOImpl.java:40)
at
com.bigg.nihonbare.common.service.impl.CommonServiceImpl.updateFamily(CommonServiceImpl.java:55)
at
com.bigg.nihonbare.util.flow.DynaRowHibernateUpdateHandler.handleRow(DynaRowHibernateUpdateHandler.java:72)
NOTE : In my DAOs I only have used methods like;
return (Long) this.getHibernateTemplate().save(family);
return (Family) this.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session
.createCriteria(Family.class);
criteria.add(Expression.eq("familyId", familyId));
if (criteria.list().size() > 0) {
return criteria.list().get(0);
}
return null;
}
});
Watch for getHibernateTemplate().getSessionFactory().openSession() in your DAO, you might be ending up opening 2 sessions, since you lately switched to this architecture, I know it from your other previous question.
Hint: Use getSession() instead. This SpringSource Forum's thread might be helpful.
There are a lot of session associated to each thread separately .session is not a
Shared variables but sessionfactory is,session is created by spring container if you inject sessionfactory into DAO.
Related
I have a question regarding #Transactional annotation.
Nothing special defined, so as I understand is PROPAGATION_REQUIRED
Let’s say I have a transactional annotation which on both service and dao layer.
Service
#Transactional
public long createStudentInDB(Student student) {
final long id = addStudentToDB (student);
addStudentToCourses (id, student.getCourseIds());
return id;
}
private long addStudentToDB (Student student) {
StudentEntity entity = new StudentEntity ();
convertToEntity(student, entity);
try {
final id = dao.create(entity);
} catch (Exception e){
//
}
return id;
}
private void addStudentToCourses (long studentId, List<String> coursesIds){
//add user to group
if(coursesIds!= null){
List<StudentCourseEntity> studentCourses = new ArrayList<>();
for(String coursesId: coursesIds){
StudentCourseEntity entity = new StudentCourseEntity ();
entity.setCourseId(coursesId);
entity.setStudentId(userId);
studentCourses.add(studentId);
}
anotherDao.saveAll(studentCourses);
}
}
DAO
#Transactional
public UUID create(StudentEntity entity) {
if ( entity == null ) { throw new Exception(//…); }
getCurrentSession().save(entity);
return entity.getId();
}
ANOTHER DAO:
#Transactional
public void saveAll(Collection< StudentCourseEntity > studentCourses) {
List< StudentCourseEntity > result = new ArrayList<>();
if(studentCourses!= null) {
for (StudentCourseEntity studentCourse : studentCourses) {
if (studentCourse!= null) {
save(studentCourse);
}
}
}
}
Despite the fact that’s not optimal, it seems it causing deadlocks.
Let’s say I have max 2 connections to the database.
And I am using 3 different threads to run the same code.
Thread-1 and thread-2 receive a connection, thread-3 is not getting any connection.
More than that, it seems that thread-1 become stuck when trying to get a connection in dao level, same for thread-2. Causing a deadlock.
I was sure that by using propagation_required this would not happen.
Am I missing something?
What’s the recommendation for something like that? Is there a way I can have #transactional on both layers? If not which is preferred?
Thanks
Fabrizio
As the dao.doSomeStuff is expected to be invoked from within other transactions I would suggest you to configure this method as:
#Transaction(propagation=REQUIRES_NEW)
Thanks to that the transaction which is invoking this method will halted until the one with REQUIRES_NEW will be finished.
Not sure if this is the fix for your particular deadlock case but your example fits this particular set-up.
You are right, Propagation.REQUIRED is the default. But that also means that the second (nested) invocation on dao joins / reuses the transaction created on service level. So there is no need to create another transaction for the nested call.
In general Spring (on high level usage) should manage resource handling by forwarding it to the underlying ORM layer:
The preferred approach is to use Spring's highest level template based
persistence integration APIs or to use native ORM APIs with
transaction- aware factory beans or proxies for managing the native
resource factories. These transaction-aware solutions internally
handle resource creation and reuse, cleanup, optional transaction
synchronization of the resources, and exception mapping. Thus user
data access code does not have to address these tasks, but can be
focused purely on non-boilerplate persistence logic.
Even if you handle it on your own (on low level API usage) the connections should be reused:
When you want the application code to deal directly with the resource
types of the native persistence APIs, you use these classes to ensure
that proper Spring Framework-managed instances are obtained,
transactions are (optionally) synchronized, and exceptions that occur
in the process are properly mapped to a consistent API.
...
If an existing transaction already has a connection synchronized
(linked) to it, that instance is returned. Otherwise, the method call
triggers the creation of a new connection, which is (optionally)
synchronized to any existing transaction, and made available for
subsequent reuse in that same transaction.
Source
Maybe you have to find out what is happening down there.
Each Session / Unit of Work will be bound to a thread and released (together with the assigned connection) after the transaction has ended. Of course when your thread gets stuck it won't release the connection.
Are you sure that this 'deadlock' is caused by this nesting? Maybe that has another reason. Do you have some test code for this example? Or a thread dump or something?
#Transactional works by keeping ThreadLocal state, which can be accessed by the (Spring managed) Proxy EntityManager. If you are using Propagation.REQUIRED (the default), and you have a non-transactional method which calls two different DAOs (or two Transactional methods on the same DAO), you will get two transactions, and two calls to acquire a pooled connection. You may get the same connection twice or two different connections, but you should only use one connection at the time.
If you call two DAOs from a #Transactional method, there will only be one transaction, as the DAO will find and join the existing transaction found in the ThreadLocal state, again you only need one connection from the pool.
If you get a deadlock then something is very wrong, and you may want to debug when your connections and transaction are created. A transaction is started by calling Connection.setAutoCommit(false), in Hibernate this happens in org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor#begin(). Connections are managed by a class extending org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor so these are some good places to put a break point, and trace the call-stack back to your code to see which lines created connections.
This question already has answers here:
Hibernate Performance Best Practice?
(3 answers)
Closed 7 years ago.
I am facing some performace issue in my application. Especially while saving to and retrieving from the database.
My application is a standalone application. Not a webapplication. I am using hibernate for database communication.
This application has not gone in production. This application will be used 50 people simultaneously.
Here is the below code which I have used for getting session. I suspect below is the code for getting slowness.
public class HibernateUtil {
public Session getSession() {
SessionFactory buildSessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
Session session = buildSessionFactory.openSession();
return session;
}
public Transaction getTransaction() {
return getSession().getTransaction();
}
}
I am not sure how to write a good code for opening session.
Can anyone suggest how to refactor this code to a good code?
Also, are the session getting closed, means once the thread has completed a process, are we closing the session.
There is no single statement answer for your question unless the code is observed, there are lot of attributes on which the performance of the system depends.
1.) GC - When and which algorithm being used.
2.) How many session are getting opened, are there any sessions which are left idle. Long running sessions should be avoided.
3.) Introducing Caching mechanism in the code, Hibernate 2nd Level cache, query caching
4.) How big the objects are getting retrieved from the database.
5.) How many queries are getting fired into DB.
Please refer this, this
I faced the similar type of issue in past, the base culprit for this is your are creating the factory instance every time when you are trying to get a transaction.
Please use the singleton pattern to create the SessionFactory object, means just create Session factory once and get all the session instance from that factory object, I am pretty sure you will not face the performance issue after this change
change Something like below-
static SessionFactory buildSessionFactory;
public static getSessionFactory() {
if(buildSessionFactory==null) {
buildSessionFactory = new
AnnotationConfiguration().configure().buildSessionFactory();
}
}
public Session getSession() {
getSessionFactory();
Session session = buildSessionFactory.openSession();
return session;
}
public Transaction getTransaction() {
return getSession().getTransaction();
}
It will solve your problem
I'm using Spring to manage transaction in my service layer with #Transactional annotation.
This is fine for eager loaded collection, but is bad for lazy loading.
I cant use OSV pattern, my application is a standalone desktop client application.
So I thought about a solution, and this is what I tried to do:
public abstract class TransactionTask {
public TransactionTask() {
}
public abstract void job();
}
In my service I have:
#Transactional
public void doJob(Transactiontask tt){
tt.job();
}
And how I use it is:
myService.doJob(new TransactionTask() {
#Override
public void job() {
//lazy code here
}
});
I expect to see this code work, becouse session is open in doJob method(it is marked as transactional), but it doesn't work...the exception is no session or session was closed.
Why this code doesn't work, and how can I load a collection only when i need it?
I do not want to write a service with a specified method to load collection, that is not a solution.
Lazy Loading doesn't require just a session, it requires the session that your Entity is attached to. You need to attach the objects to the new session created by #Transactional before you try to access the lazy fields.
Also, while you obviously cannot use one of the available OSIV servlet filters, the 'pattern' is still valid. There has to be some definable scope that you can couple the lifecycle of a session to. (It may prove to be so large that there are other challenges making it not worth the effort, but it is still an option.)
E.g. you have User user object. It's detached that's why you got "the exception is no session or session was closed".
You can reread it from the dao user=userDao.findById(user.getId()) and then get lazy collections of the newly retrieved object.
As I understand when I use hibernate with Spring transactions, a session is bound to thread using ThreadLocal. My questions are:
How can I access the session or session factory (without injecting it to the bean) (thus by thread locale)?
2.How can I do the same in terms of JPA, thus using EnityManager / EntityManagerFactory?
You should use the SessionFactory.getCurrentSession() to get your Session. What the session returned from this method depends on the configuration parameter hibernate.current_session_context_class in the hibernate.cfg.xml . If it is set to thread , the returned session is get from the ThreadLocal , which means that if it is called for the first time in the current Java thread, a new Session is opened and returned . If it is called again in the same thread , the same session will be returned.
AFAIK , there are no equivalent SessionFactory.getCurrentSession() in JPA . I think you have manually set and get the EntityManager to the ThreadLocal
Reference
Hibernate Contextual sessions
Sessions and transactions
I was trying to figure this out today and ended up doing it this way:
#PersistenceContext(unitName = "jpaSessionFactoryName")
private EntityManager jpaSession;
I found this documentation very helpful:
https://docs.spring.io/spring/docs/4.3.2.RELEASE/spring-framework-reference/htmlsingle/#orm-jpa-straight
When you use #PersistenceContext, Spring injects a proxy that gives you an EntityManager bound to the current transaction (or a new one, if there is not any). This looks like a direct substitute for hibernate's:
sessionFactory.getCurrentSession();
which is exactly what I was looking for.
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.