Note that there are some explanatory texts on larger screens.

plurals
  1. POHibernate/Spring: failed to lazily initialize - no session or session was closed
    primarykey
    data
    text
    <p><strong>For an answer scroll down to the end of this...</strong></p> <p>The basic problem is the same as asked multiple time. I have a simple program with two POJOs Event and User - where a user can have multiple events.</p> <pre><code>@Entity @Table public class Event { private Long id; private String name; private User user; @Column @Id @GeneratedValue public Long getId() {return id;} public void setId(Long id) { this.id = id; } @Column public String getName() {return name;} public void setName(String name) {this.name = name;} @ManyToOne @JoinColumn(name="user_id") public User getUser() {return user;} public void setUser(User user) {this.user = user;} } </code></pre> <p>The User:</p> <pre><code>@Entity @Table public class User { private Long id; private String name; private List&lt;Event&gt; events; @Column @Id @GeneratedValue public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Column public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(mappedBy="user", fetch=FetchType.LAZY) public List&lt;Event&gt; getEvents() { return events; } public void setEvents(List&lt;Event&gt; events) { this.events = events; } } </code></pre> <p>Note: This is a sample project. I <strong>really</strong> want to use Lazy fetching here.</p> <p>Now we need to configure spring and hibernate and have a simple basic-db.xml for loading:</p> <pre> <code> &lt;?xml version="1.0" encoding="UTF-8"?> &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> &lt;bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" scope="thread"> &lt;property name="driverClassName" value="com.mysql.jdbc.Driver" /> &lt;property name="url" value="jdbc:mysql://192.168.1.34:3306/hibernateTest" /> &lt;property name="username" value="root" /> &lt;property name="password" value="" /> &lt;aop:scoped-proxy/> &lt;/bean> &lt;bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> &lt;property name="scopes"> &lt;map> &lt;entry key="thread"> &lt;bean class="org.springframework.context.support.SimpleThreadScope" /> &lt;/entry> &lt;/map> &lt;/property> &lt;/bean> &lt;bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" scope="thread"> &lt;property name="dataSource" ref="myDataSource" /> &lt;property name="annotatedClasses"> &lt;list> &lt;value>data.model.User&lt;/value> &lt;value>data.model.Event&lt;/value> &lt;/list> &lt;/property> &lt;property name="hibernateProperties"> &lt;props> &lt;prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect&lt;/prop> &lt;prop key="hibernate.show_sql">true&lt;/prop> &lt;prop key="hibernate.hbm2ddl.auto">create&lt;/prop> &lt;/props> &lt;/property> &lt;aop:scoped-proxy/> &lt;/bean> &lt;bean id="myUserDAO" class="data.dao.impl.UserDaoImpl"> &lt;property name="sessionFactory" ref="mySessionFactory" /> &lt;/bean> &lt;bean id="myEventDAO" class="data.dao.impl.EventDaoImpl"> &lt;property name="sessionFactory" ref="mySessionFactory" /> &lt;/bean> &lt;/beans> </code> </pre> <p>Note: I played around with the CustomScopeConfigurer and SimpleThreadScope, but that didnt change anything.</p> <p>I have a simple dao-impl (only pasting the userDao - the EventDao is pretty much the same - except with out the "listWith" function:</p> <pre> <code> public class UserDaoImpl implements UserDao{ private HibernateTemplate hibernateTemplate; public void setSessionFactory(SessionFactory sessionFactory) { this.hibernateTemplate = new HibernateTemplate(sessionFactory); } @SuppressWarnings("unchecked") @Override public List listUser() { return hibernateTemplate.find("from User"); } @Override public void saveUser(User user) { hibernateTemplate.saveOrUpdate(user); } @Override public List listUserWithEvent() { List users = hibernateTemplate.find("from User"); for (User user : users) { System.out.println("LIST : " + user.getName() + ":"); user.getEvents().size(); } return users; } } </code> </pre> <p>I am getting the org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at the line with <em>user.getEvents().size()</em>;</p> <p>And last but not least here is the Test class I use:</p> <pre> <code> public class HibernateTest { public static void main(String[] args) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml"); UserDao udao = (UserDao) ac.getBean("myUserDAO"); EventDao edao = (EventDao) ac.getBean("myEventDAO"); System.out.println("New user..."); User user = new User(); user.setName("test"); Event event1 = new Event(); event1.setName("Birthday1"); event1.setUser(user); Event event2 = new Event(); event2.setName("Birthday2"); event2.setUser(user); udao.saveUser(user); edao.saveEvent(event1); edao.saveEvent(event2); List users = udao.listUserWithEvent(); System.out.println("Events for users"); for (User u : users) { System.out.println(u.getId() + ":" + u.getName() + " --"); for (Event e : u.getEvents()) { System.out.println("\t" + e.getId() + ":" + e.getName()); } } ((ConfigurableApplicationContext)ac).close(); } } </code> </pre> <p>and here is the Exception:</p> <pre> 1621 [main] ERROR org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380) at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372) at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119) at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248) at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38) at HibernateTest.main(HibernateTest.java:44) Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380) at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372) at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119) at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248) at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38) at HibernateTest.main(HibernateTest.java:44) </pre> <p>Things tried but did not work:</p> <ul> <li>assign a threadScope and using beanfactory (I used "request" or "thread" - no difference noticed):</li> </ul> <pre> // scope stuff Scope threadScope = new SimpleThreadScope(); ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory(); beanFactory.registerScope("request", threadScope); ac.refresh(); ... </pre> <ul> <li>Setting up a transaction by getting the session object from the deo:</li> </ul> <pre> ... Transaction tx = ((UserDaoImpl)udao).getSession().beginTransaction(); tx.begin(); users = udao.listUserWithEvent(); ... </pre> <ul> <li>getting a transaction within the listUserWithEvent()</li> </ul> <pre> public List listUserWithEvent() { SessionFactory sf = hibernateTemplate.getSessionFactory(); Session s = sf.openSession(); Transaction tx = s.beginTransaction(); tx.begin(); List users = hibernateTemplate.find("from User"); for (User user : users) { System.out.println("LIST : " + user.getName() + ":"); user.getEvents().size(); } tx.commit(); return users; } </pre> <p>I am really out of ideas by now. Also, using the listUser or listEvent just work fine. </p> <p><strong>Step forward:</strong></p> <p>Thanks to Thierry I got one step further (I think). I created the MyTransaction class and do my whole work in there, getting everything from spring. The new main looks like this:</p> <pre> <code> public static void main(String[] args) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml"); // getting dao UserDao udao = (UserDao) ac.getBean("myUserDAO"); EventDao edao = (EventDao) ac.getBean("myEventDAO"); // gettting transaction template TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplate"); MyTransaction mt = new MyTransaction(udao, edao); transactionTemplate.execute(mt); ((ConfigurableApplicationContext)ac).close(); } </code> </pre> <p>Unfortunately now there is a null-pointer Exception @: user.getEvents().size(); (in the daoImpl). </p> <p>I know that it should not be null (neither from the output in the console nor from the db layout). </p> <p>Here is the console output for more information (I did a check for user.getEvent() == null and printed "EVENT is NULL"):</p> <pre> New user... Hibernate: insert into User (name) values (?) Hibernate: insert into User (name) values (?) Hibernate: insert into Event (name, user_id) values (?, ?) Hibernate: insert into Event (name, user_id) values (?, ?) Hibernate: insert into Event (name, user_id) values (?, ?) List users: Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_ 1:User1 2:User2 List events: Hibernate: select event0_.id as id1_, event0_.name as name1_, event0_.user_id as user3_1_ from Event event0_ 1:Birthday1 for 1:User1 2:Birthday2 for 1:User1 3:Wedding for 2:User2 Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_ Events for users 1:User1 -- EVENT is NULL 2:User2 -- EVENT is NULL </pre> <p>You can get the sample project from <a href="http://www.gargan.org/code/hibernate-test1.tgz" rel="nofollow noreferrer">http://www.gargan.org/code/hibernate-test1.tgz</a> (it's an eclipse/maven project)</p> <p><strong>The solution (for console applications)</strong></p> <p>There are actually two solutions for this problem - depending on your environment:</p> <p>For a console application you need a transaction template which captures the actutal db logic and takes care of the transaction:</p> <pre> <code> public class UserGetTransaction implements TransactionCallback{ public List users; protected ApplicationContext context; public UserGetTransaction (ApplicationContext context) { this.context = context; } @Override public Boolean doInTransaction(TransactionStatus arg0) { UserDao udao = (UserDao) ac.getBean("myUserDAO"); users = udao.listUserWithEvent(); return null; } } </code> </pre> <p>You can use this by calling:</p> <pre> <code> TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate"); UserGetTransaction mt = new UserGetTransaction(context); transactionTemplate.execute(mt); </code> </pre> <p>In order for this to work you need to define the template class for spring (ie. in your basic-db.xml):</p> <pre><code>&lt;bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"&gt; &lt;property name="transactionManager" ref="transactionManager"/&gt; &lt;/bean&gt; </code></pre> <p><strong>Another (possible) solution</strong></p> <p>thanks andi</p> <pre><code> PlatformTransactionManager transactionManager = (PlatformTransactionManager) applicationContext.getBean("transactionManager"); DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED); transactionAttribute.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); TransactionStatus status = transactionManager.getTransaction(transactionAttribute); boolean success = false; try { new UserDataAccessCode().execute(); success = true; } finally { if (success) { transactionManager.commit(status); } else { transactionManager.rollback(status); } } </code></pre> <p><strong>The solution (for servlets)</strong></p> <p>Servlets are not that big of a problem. When you have a servlet you can simply start and bind a transaction at the beginning of your function and unbind it again at the end:</p> <pre><code>public void doGet(...) { SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory"); Session session = SessionFactoryUtils.getSession(sessionFactory, true); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); // Your code.... TransactionSynchronizationManager.unbindResource(sessionFactory); } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload