Spring-Hibernate : Illegal attempt to associate a collection with two open sessions
Asked Answered
W

7

17

I am trying to update record in MySql Db. while updating it threw following exception

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.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:89)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
at com.tcs.ignite.ih.spring.dao.UserDAOImpl.saveUpdateUserbean(UserDAOImpl.java:185)
at com.tcs.ignite.ih.spring.dao.UserDAOImpl.blockuser(UserDAOImpl.java:204)
at com.tcs.ignite.ih.spring.service.UserServiceImpl.blockUser(UserServiceImpl.java:187)
at com.tcs.ignite.ih.spring.controller.AdminHomeController.BlockUser(AdminHomeController.java:48)

I check for the session. Its closes in Finally block of every methode . Not able to figure out what is wrong. I am able to to insertupdate opertion with other methods without any problem but only saveUpdateUserBean method is throwing exception

UserDAOImpl:

import com.tcs.ignite.ih.hibernate.model.Userdetails;
import com.tcs.ignite.ih.hibernate.model.Userlog;
import com.tcs.ignite.ih.hibernate.model.Userrole;
import com.tcs.ignite.ih.spring.bean.LoginBean;
import com.tcs.ignite.ih.spring.util.LogFile;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;


@Repository
@Transactional
public class UserDAOImpl implements UserDAO {

@Autowired
private SessionFactory sessionFactory;

public SessionFactory getSessionFactory() {
    return sessionFactory;
}

public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
}


@Override
public Userdetails getUserDetails(String username) {
    Session session = getSessionFactory().openSession();
    Userdetails u = null;
    try {
        u = (Userdetails) getSessionFactory().openSession()
                .createCriteria(Userdetails.class)
                .add(Restrictions.eq("email", username)).uniqueResult();
    } catch (Exception e) {
        LogFile.log.error("UserDAO getuserDetails(): " + e.toString());
    } finally {
        if (session.isOpen()) {
            session.close();
        }
        return u;
    }
}

@Override
public boolean saveUpdateUserbean(Userdetails u) {

    Session session = getSessionFactory().openSession();
    Transaction tr = session.beginTransaction();
    boolean y = false;
    try {
        session.saveOrUpdate(u);
        tr.commit();
        y = true;
    } catch (Exception e) {
        tr.rollback();
        e.printStackTrace();
    } finally {
        if (session.isOpen()) {
            session.close();
        }
        return y;
    }
}

@Override
public boolean blockuser(String email) {
    Userdetails u = this.getUserDetails(email);
    return this.saveUpdateUserbean(u);
}
}

ServiceImpl:

    import com.tcs.ignite.ih.hibernate.model.Userdetails;
import com.tcs.ignite.ih.hibernate.model.Userlog;
import com.tcs.ignite.ih.spring.bean.LogBean;
import com.tcs.ignite.ih.spring.bean.RegisterBean;
import com.tcs.ignite.ih.spring.bean.UserBean;
import com.tcs.ignite.ih.spring.bean.loadUserBean;
import com.tcs.ignite.ih.spring.dao.UserDAO;
import com.tcs.ignite.ih.spring.util.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

@Autowired
UserDAO dao;

@Override
public boolean blockUser(String email) {
   return dao.blockuser(email);
}
}

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">

<bean id="propertyConfigurer"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
      p:location="classpath:jdbc.properties" />

<bean id="dataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource"
      p:driverClassName="${jdbc.driverClassName}"
      p:url="${jdbc.url}"
      p:username="${jdbc.username}"
      p:password="${jdbc.password}"/>

<!-- ADD PERSISTENCE SUPPORT HERE (jpa, hibernate, etc) -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="com.tcs.ignite.ih.hibernate.model" />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
        </props>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/> 
</bean>
<tx:annotation-driven />
</beans>

I am able to perform all database operation using same configuration but when i am calling blockuser() method from serviceImpl its calling DAO methods and the saveupdateUserBean is throwing above exception? m i missing anything?

Whitehorse answered 16/4, 2014 at 11:20 Comment(0)
C
18

Hibernate manual says:

Use update() if you are certain that the session does not contain an already persistent instance with the same identifier. Use merge() if you want to merge your modifications at any time without consideration of the state of the session. In other words, update() is usually the first method you would call in a fresh session, ensuring that the reattachment of your detached instances is the first operation that is executed.

It helped in my case. DAO:

public void updateUser(User user) throws UserException {
    sessionFactory.getCurrentSession().merge(user);
}

POJO Ads (One User has many ads):

@OneToMany(mappedBy = "oUser", fetch = FetchType.LAZY)
public List<Ad> getAoAdList() {
    return aoAdList;
}  
Convexity answered 29/10, 2015 at 7:30 Comment(0)
S
6

Use the built-in session tool:

sessionFactory.getCurrentSession()

Don't manually open and close them yourself.

The collection is attempting to be associated with two sessions. Also SessionFactory, although perfectly valid, is not part of JPA. JPA relies on EntityFactory.

Your methods, because you define the class as transactional, do not require manually starting a transaction. Remove this (and any reference to transactions) from saveorUpdate.

Transaction tr = session.beginTransaction();

Transactions conventionally go on the service layer, not the repositories. So you can wrap multiple repository/DAO calls in one service layer method that is transactional.

Snaggletooth answered 16/4, 2014 at 11:33 Comment(6)
can you please explain. how can i open session implicitly?Whitehorse
@igniter um yeah, I got ahead of myself see update. You define class as transactional using an annotation - no need to open a transaction in code.Snaggletooth
i have other methods also in same daoImpl. do i need to remove all transactions from those methods also? and how can i commit saveupdate without transaction?Whitehorse
or remove the annotation ... only have one transaction thats the error. There are differetn types of transactional annotations. Require new, readonly etc. I normally define them on each method as required.Snaggletooth
i tried without @Transactional annotation. but still same exception is there.Whitehorse
@igniter you also need to change to use sessionFactory.getCurrentSession() see updateSnaggletooth
S
3

problem might if u use below code

instead of using getSession() if you use `

getHibernateTemplate().getSessionFactory().openSession(), causes two sessions to be opened at the sametime.

Syzran answered 16/4, 2014 at 12:43 Comment(0)
T
3

The problem was due to a misuse of cascade update in one of the mappings. Here is a sample field mapping:

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = “user_id”)
public User getAuthor() {
return author;
}

Removing the cascade = CascadeType.ALL fixed the problem. Conclusion: carefully use cascade updates as it may get you into trouble. Use it when business logic requires it. In the example below there was no need for it, so removing it was both business and programatically a good decision.

Source: http://blog.tremend.ro/2007/03/05/illegal-attempt-to-associate-a-collection-with-two-open-sessions/

Turpentine answered 19/12, 2014 at 12:3 Comment(1)
and when logic require it? does my case fit? cause i didn't use it and i get the error: #28129577Annulose
S
0

I had a similar issue as you and I found no solution to my specific Problem. I had to close the Session on the collection manually with:

((PersistentSet)myObject.getCollection()).getSession().close();

I guess it is not good Practice to solve the issue like this, but for me it was the only way.

EDIT: Of course this only works on a Set not if the Collection is a List or sth. else...

Shantell answered 20/7, 2017 at 21:23 Comment(0)
L
0

This can happen if you are creating or updating an existing record that contains an entity that was loaded from a different session. The error is deceptive because its the relationship on that entity that triggers the error. The solution is to load a fresh entity within the current session and use that instead of the argument that was passed in from a different session.

entity = getHibernateTemplate().load(MyEntity.class, entity.getId())
Lancashire answered 19/7, 2021 at 3:24 Comment(0)
S
0

If u want to open session each action(CRUD) u should put Session session = sessionFactory.openSession()

in try catch like this : Try(Session session = sessionFactory.openSession()) , then session will auto close whenever it done the job, this may basic concept but it took me 1 hour to figure out

Sluice answered 1/4, 2023 at 2:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.