休眠/ Spring:未能懒惰地初始化 - 没有会话或会话被关闭 [英] Hibernate/Spring: failed to lazily initialize - no session or session was closed

查看:124
本文介绍了休眠/ Spring:未能懒惰地初始化 - 没有会话或会话被关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本问题与多次询问相同。我有一个包含两个POJO事件和用户的简单程序 - 用户可以有多个事件。

  @Entity 
@Table
public class Event {
private Long id;
私人字符串名称;
私人用户用户;

@Column
@Id
@GeneratedValue
public Long getId(){return id;}
public void setId(Long id){this。 id = id; }
$ b @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;}

$ b $ / code>

用户:

  @Entity 
@Table
public class User {
private Long id;
私人字符串名称;
私人列表< Event>事件;

@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< Event> getEvents(){return events; }
public void setEvents(List< Event> events){this.events = events; }

}

注意:这是一个示例项目。我真的很想在这里使用Lazy fetching。

现在我们需要配置spring和hibernate并且有一个简单的basic-db.xml来加载:

 

<?xml version =1.0encoding =UTF-8? >
< 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


$ bean $ = b destroy-method =closescope =thread>








< property name =scopes>

< entry key =thread>
< bean class =org.springframework.context .support.SimpleThreadScope/>
< / entry>
< / map>
< / property>
< / bean>

< bean id =mySessionFactory
class =org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBeanscope =thread>

< property name =annotatedClasses>
< list>
< value> data.model.User< / value>
< value>数据.model.Event< / value>
< / list>
< / property>
< property name =hibernateProperties>
< pr ops>
< prop key =hibernate.dialect> org.hibernate.dialect.MySQLDialect< / prop>
< prop key =hibernate.show_sql> true< / prop>
< prop key =hibernate.hbm2ddl.auto> create< / prop>
< / props>
< / property>
< aop:scoped-proxy / >

< / bean>

< bean id =myUserDAOclass =data.dao.impl.UserDaoImpl>
<属性名称=sessionFactoryref =mySessionFactory/>
< / bean>

< bean id =myEventDAOclass =data.dao.impl.EventDaoImpl>
< property name =sessionFactoryref =mySessionFactory/>


< / beans>

注意:我使用了CustomScopeConfigurer和SimpleThreadScope,但是这并没有改变任何东西。



我有一个简单的dao-impl(只粘贴userDao - EventDao几乎相同 - 除了listWith函数外:

 

public class Use rDaoImpl实现UserDao {

private HibernateTemplate hibernateTemplate;

public void setSessionFactory(SessionFactory sessionFactory){
this.hibernateTemplate = new HibernateTemplate(sessionFactory);


$ b @SuppressWarnings(unchecked)
@Override
public List listUser(){
return hibernateTemplate.find(来自用户);
}

@Override
public void saveUser(User user){
hibernateTemplate.saveOrUpdate(user);


$ b @Override
public List listUserWithEvent(){

List users = hibernateTemplate.find(from User);
for(User user:users){
System.out.println(LIST:+ user.getName()+:);
user.getEvents()。size();
}
返回用户;
}

}

org.hibernate.LazyInitializationException - 无法延迟初始化角色集合:data.model.User.events,没有会话或会话在 user.getEvents()。size()行关闭。 ;



最后但并非最不重要的是我使用的Test类:

 

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);

事件event2 = new Event();
event2.setName(Birthday2);
event2.setUser(user);

udao.saveUser(user);
edao.saveEvent(event1);
edao.saveEvent(event2);

列出users = udao.listUserWithEvent();
System.out.println(用户事件);
for(User u:users){

System.out.println(u.getId()+:+ u.getName()+ - );
for(Event e:u.getEvents())
{
System.out.println(\ t+ e.getId()+:+ e.getName )); $(b



((ConfigurableApplicationContext)ac).close();
}

}

这里是例外:

 
1621 [main]错误org.hibernate.LazyInitializationException - 无法延迟初始化角色集合:data.model.User .events,没有会话或会话已关闭
org.hibernate.LazyInitializationException:无法懒惰地初始化角色集合:data.model.User.events,没有会话或会话已关闭
在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)
在希伯恩ateTest.main(HibernateTest.java:44)
线程main中的异常org.hibernate.LazyInitializationException:无法延迟初始化角色集合:data.model.User.events,没有会话或会话已关闭
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
at org.hibernate。
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl。)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl。在HibernateTest.main(HibernateTest.java:44)





事情已经过去了,但没有成功: p>


  • 分配一个threadScope并使用beanfactory(我使用request或thread - 没有区别):



 
//范围内容
范围threadScope = new SimpleThreadScope();
ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory();
beanFactory.registerScope(request,threadScope);
ac.refresh();
...




  • 通过从deo获取会话对象来设置事务:



 
...
Transaction tx =((UserDaoImpl)udao).getSession()。的BeginTransaction();
tx.begin();
users = udao.listUserWithEvent();
...




  • 在listUserWithEvent()中获取事务



 
public List listUserWithEvent(){
SessionFactory sf = hibernateTemplate.getSessionFactory();
Session s = sf.openSession();
Transaction tx = s.beginTransaction();
tx.begin();

列出users = hibernateTemplate.find(from User);
for(User user:users){
System.out.println(LIST:+ user.getName()+:);
user.getEvents()。size();
}
tx.commit();
返回用户;
}

现在我真的没有想法。此外,使用listUser或listEvent只是工作正常。



前进:

感谢Thierry,我又迈进了一步认为)。我创建了MyTransaction类,并在那里完成了我的全部工作,从春季开始获取所有内容。新的主要看起来像这样:

 

public static void main(String [] args){

ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(basic-db.xml);

//获取dao
UserDao udao =(UserDao)ac.getBean(myUserDAO);
EventDao edao =(EventDao)ac.getBean(myEventDAO);

//获取事务模板
TransactionTemplate transactionTemplate =(TransactionTemplate)ac.getBean(transactionTemplate);

MyTransaction mt = new MyTransaction(udao,edao);
transactionTemplate.execute(mt);

((ConfigurableApplicationContext)ac).close();


不幸的是,现在有一个空指针Exception @:user 。.getEvents()大小(); (在daoImpl中)。

我知道它不应该是null(既不来自控制台的输出,也不来自数据库布局)。



以下是控制台输出的更多信息(我对user.getEvent()== null进行了检查并打印出EVENT is NULL):

 
新用户...
Hibernate:插入User(name)值(?)
Hibernate:插入User )值(?)
Hibernate:插入Event(name,user_id)值(?,?)
Hibernate:插入Event(name,user_id)值(?,?)
Hibernate :插入Event(name,user_id)值(?,?)
列出用户:
Hibernate:选择user0_.id作为id0_,user0_.name作为name0_从用户user0_
1:User1
2:User2
列出事件:
Hibernate:选择event0_.id作为id1_,event0_.name作为name1_,event0_.user_id作为user3_1_来自事件event0_
1:Birthday1 for 1 :User1
2:Birthday2 for 1:User1
3:Wedding for 2:User2
Hibernate:选择user0_.id作为id0_,user0_.name作为name0_从用户user0_
事件用户
1:User1 -
EVENT为NULL
2:User2 -
EVENT为NULL

您可以从 http: //www.gargan.org/code/hibernate-test1.tgz (这是一个eclipse / maven项目)



解决方案(用于控制台应用程序)



这个问题实际上有两种解决方案 - 取决于您的环境:



对于控制台应用程序,您需要一个事务模板来捕获实际的数据库逻辑并处理事务:

 

public class UserGetTransaction实现TransactionCallback {

public List users;

保护ApplicationContext上下文;

public UserGetTransaction(ApplicationContext context){
this.context = context;

$ b @Override
public Boolean doInTransaction(TransactionStatus arg0){
UserDao udao =(UserDao)ac.getBean(myUserDAO);
users = udao.listUserWithEvent();
返回null;
}

}

您可以使用这通过调用:

 

TransactionTemplate transactionTemplate =(TransactionTemplate)context.getBean(transactionTemplate) ;
UserGetTransaction mt = new UserGetTransaction(context);
transactionTemplate.execute(mt);

为了达到这个目的,你需要为spring定义模板类在你的basic-db.xml中):

 < bean id =transactionTemplateclass =org.springframework.transaction .support.TransactionTemplate> 
< property name =transactionManagerref =transactionManager/>
< / bean>

另一个(可能的)解决方案

感谢andi

  PlatformTransactionManager transactionManager =(PlatformTransactionManager)applicationContext.getBean(transactionManager); 
DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);

transactionAttribute.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
TransactionStatus status = transactionManager.getTransaction(transactionAttribute);
布尔成功= false;
尝试{
new UserDataAccessCode()。execute();
成功= true;
} finally {
if(success){
transactionManager.commit(status);
} else {
transactionManager.rollback(status);




解决方案(对于servlets) strong>



Servlets并不是什么大问题。当你有一个servlet时,你可以简单地在你的函数开始处启动并绑定一个事务,并在最后解除绑定:

  public void doGet(...){
SessionFactory sessionFactory =(SessionFactory)context.getBean(sessionFactory);
Session session = SessionFactoryUtils.getSession(sessionFactory,true);
TransactionSynchronizationManager.bindResource(sessionFactory,new SessionHolder(session));

//您的代码....

TransactionSynchronizationManager.unbindResource(sessionFactory);


解决方案

我认为你不应该使用hibernate会话事务处理方法,但让spring做到这一点。



添加到您的spring conf:

 < bean id =txManagerclass =org.springframework.orm.hibernate3.HibernateTransactionManager> 
< property name =sessionFactoryref =mySessionFactory/>
< / bean>

< bean id =transactionTemplateclass =org.springframework.transaction.support.TransactionTemplate>
< property name =transactionManagerref =txManager/>
< / bean>

然后我会修改您的测试方法以使用spring事务模板:

  public static void main(String [] args){
// init here(获取dao和事务模板)

transactionTemplate.execute(new TransactionCallback(){
@Override
public Object doInTransaction(TransactionStatus status){
//在这里做你的休眠的东西:调用保存,列表方法等



code


作为备注,@OneToMany (默认情况下,@ ToMany是LAZY,@ * ToOne默认是EAGER)



编辑:从hibernate的角度来看,现在发生了什么:


  • 打开会话(使用事务开始)

  • 保存用户并将其保存在会话中(请参阅会话缓存作为实体hashmap,其中的密钥是实体ID) li>
  • 保存事件并将其保存在会话中 p> ...与所有保存操作相同......
  • 然后加载所有用户(来自用户的查询)


  • hibernate在它的会话中已经看到它已经有了这个对象,所以丢弃它从请求中获得的那个,并从会话中返回。

  • 您的会话中的用户没有初始化其事件集合,因此您将获得空值。

  • ...



以下几点可以增强您的代码:




  • 不需要集合排序时,可以使用在你的模型中设置,而不是你的收藏列表(私人设置事件,而不是私人列表事件)
  • ,键入你的集合,否则hibernate不会获取哪个实体(private Set< Event> ;事件)当你设置双向关系的一边时,你希望在同一事务中使用关系的mappedBy一侧,设置双方。在下一个tx之前,Hibernate不会为你做这件事(当会话是从db状态的全新视图时)。



解决上面的问题,要么保存在一个事务中,而要在另一个事务中加载:

  public static void main(String (){
//在这里初始化(获取dao和事务模板)
transactionTemplate.execute(new TransactionCallback(){
@Override $ b $ public Object doInTransaction(TransactionStatus status) {
//在这里保存
}
}

transactionTemplate.execute(new TransactionCallback(){
@Override
public Object doInTransaction TransactionStatus状态){
//在这里列出
}
}
}

或设定双方:

  ... 
event1.setUser(user);
...
event2.setUser(user);
...
user.setEvents(Arrays.asList(eve NT1,事件2));
...

(另外不要忘记说明上面的代码增强点,Set不是列表,集合键入)


For an answer scroll down to the end of this...

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.

@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;}

}

The User:

@Entity
@Table
public class User {
 private Long id;
 private String name;
 private List<Event> 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<Event> getEvents() { return events; }
 public void setEvents(List<Event> events) { this.events = events; }

}

Note: This is a sample project. I really want to use Lazy fetching here.

Now we need to configure spring and hibernate and have a simple basic-db.xml for loading:


<?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: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">


 <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
  destroy-method="close"  scope="thread">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://192.168.1.34:3306/hibernateTest" />
  <property name="username" value="root" />
  <property name="password" value="" />
  <aop:scoped-proxy/>
 </bean>

 <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
  <property name="scopes">
   <map>
    <entry key="thread">
     <bean class="org.springframework.context.support.SimpleThreadScope" />
    </entry>
   </map>
  </property>
 </bean>

 <bean id="mySessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" scope="thread">
  <property name="dataSource" ref="myDataSource" />
  <property name="annotatedClasses">
   <list>
    <value>data.model.User</value>
    <value>data.model.Event</value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.hbm2ddl.auto">create</prop>
   </props>
  </property>
  <aop:scoped-proxy/>

 </bean>

 <bean id="myUserDAO" class="data.dao.impl.UserDaoImpl">
  <property name="sessionFactory" ref="mySessionFactory" />
 </bean>

 <bean id="myEventDAO" class="data.dao.impl.EventDaoImpl">
  <property name="sessionFactory" ref="mySessionFactory" />
 </bean>

</beans>

Note: I played around with the CustomScopeConfigurer and SimpleThreadScope, but that didnt change anything.

I have a simple dao-impl (only pasting the userDao - the EventDao is pretty much the same - except with out the "listWith" function:


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;
 }

}

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 user.getEvents().size();

And last but not least here is the Test class I use:


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();
 }

}

and here is the Exception:

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)

Things tried but did not work:

  • assign a threadScope and using beanfactory (I used "request" or "thread" - no difference noticed):

  // scope stuff
  Scope threadScope = new SimpleThreadScope();
  ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory();
  beanFactory.registerScope("request", threadScope);
  ac.refresh();
...

  • Setting up a transaction by getting the session object from the deo:

...
  Transaction tx = ((UserDaoImpl)udao).getSession().beginTransaction();
  tx.begin();
  users = udao.listUserWithEvent();
...

  • getting a transaction within the listUserWithEvent()

 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;
 }

I am really out of ideas by now. Also, using the listUser or listEvent just work fine.

Step forward:

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:


 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();
 }

Unfortunately now there is a null-pointer Exception @: user.getEvents().size(); (in the daoImpl).

I know that it should not be null (neither from the output in the console nor from the db layout).

Here is the console output for more information (I did a check for user.getEvent() == null and printed "EVENT is NULL"):

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

You can get the sample project from http://www.gargan.org/code/hibernate-test1.tgz (it's an eclipse/maven project)

The solution (for console applications)

There are actually two solutions for this problem - depending on your environment:

For a console application you need a transaction template which captures the actutal db logic and takes care of the transaction:


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;
 }

}

You can use this by calling:


 TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate");
 UserGetTransaction mt = new UserGetTransaction(context);
 transactionTemplate.execute(mt);

In order for this to work you need to define the template class for spring (ie. in your basic-db.xml):

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

Another (possible) solution

thanks andi

    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);
      }
    }

The solution (for servlets)

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:

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);
}

解决方案

I think you should not use the hibernate session transactional methods, but let spring do that.

Add this to your spring conf:

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="mySessionFactory" />
</bean>

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="txManager"/>
</bean>

and then I would modify your test method to use the spring transaction template:

public static void main(String[] args) {
    // init here (getting dao and transaction template)

    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // do your hibernate stuff in here : call save, list method, etc
        }
    }
}

as a side note, @OneToMany associations are lazy by default, so you don't need to annotate it lazy. (@*ToMany are LAZY by default, @*ToOne are EAGER by default)

EDIT: here is now what is happening from hibernate point of view:

  • open session (with transaction start)
  • save a user and keep it in the session (see the session cache as an entity hashmap where the key is the entity id)
  • save an event and keep it in the session
  • save another event and keep it in the session
  • ... same with all the save operations ...

  • then load all users (the "from Users" query)

  • at that point hibernate see that it has already the object in its session, so discard the one it got from the request and return the one from the session.
  • your user in the session does not have its event collection initialized, so you get null.
  • ...

Here are some points to enhance your code:

  • in your model, when collection ordering is not needed, use Set, not List for your collections (private Set events, not private List events)
  • in your model, type your collections, otherwise hibernate won't which entity to fetch (private Set<Event> events)
  • when you set one side of a bidirectional relation, and you wish to use the mappedBy side of the relation in the same transaction, set both sides. Hibernate will not do it for you before the next tx (when the session is a fresh view from the db state).

So to address the point above, either do the save in one transaction, and the loading in another one :

public static void main(String[] args) {
    // init here (getting dao and transaction template)
    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // save here
        }
    }

    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // list here
        }
    }
}

or set both sides:

...
event1.setUser(user);
...
event2.setUser(user);
...
user.setEvents(Arrays.asList(event1,event2));
...

(Also do not forget to address the code enhancement points above, Set not List, collection typing)

这篇关于休眠/ Spring:未能懒惰地初始化 - 没有会话或会话被关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆