要获得答案,请向下滚动到此内容的结尾…
基本问题与多次询问相同。我有一个带有两个POJO事件和用户的简单程序-一个用户可以拥有多个事件。
@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;} }
用户:
@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; } }
注意:这是一个示例项目。我 真的 很想在这里使用Lazy抓取。
现在我们需要配置spring和hibernate,并有一个简单的basic-db.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: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>
注意:我玩过CustomScopeConfigurer和SimpleThreadScope,但是并没有改变任何东西。
我有一个简单的dao-impl(仅粘贴userDao-EventDao几乎相同-除了没有“ listWith”函数:
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; } }
我正在收到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); 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(); } }
这是例外:
1621 [main]错误org.hibernate.LazyInitializationException-无法延迟初始化角色集合:data.model.User.events,没有会话或会话被关闭 org.hibernate.LazyInitializationException:无法延迟初始化角色集合:data.model.User.events,没有会话或会话被关闭 在org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380) 在org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372) 在org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119) 在org.hibernate.collection.PersistentBag.size(PersistentBag.java:248) 在data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38) 在HibernateTest.main(HibernateTest.java:44) 线程“主”中的异常org.hibernate.LazyInitializationException:无法延迟初始化角色集合:data.model.User.events,未关闭任何会话或会话 在org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380) 在org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372) 在org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119) 在org.hibernate.collection.PersistentBag.size(PersistentBag.java:248) 在data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38) 在HibernateTest.main(HibernateTest.java:44)
事情尝试了但没有用:
分配一个threadScope并使用beanfactory(我使用了“ request”或“ thread”-没有区别):
//作用域的东西 范围threadScope = new SimpleThreadScope(); ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory(); beanFactory.registerScope(“ request”,threadScope); ac.refresh(); …
通过从Deo获取会话对象来设置事务:
… 事务tx =((UserDaoImpl)udao).getSession()。beginTransaction(); tx.begin(); 用户= udao.listUserWithEvent(); …
在listUserWithEvent()中获取交易
公共列表listUserWithEvent(){ SessionFactory sf = hibernateTemplate.getSessionFactory(); 会话s = sf.openSession(); 交易tx = s.beginTransaction(); tx.begin();
列出用户= hibernateTemplate.find(“来自用户”); 对于(用户用户:用户){ System.out.println(“ LIST:” + user.getName()+“:”); user.getEvents()。size(); } tx.commit(); 回头客; }
到目前为止,我真的没有主意。另外,使用listUser或listEvent也可以正常工作。
向前一步:
多亏蒂埃里(Thierry),我才迈出了一步。我创建了MyTransaction类,并在那里做所有工作,从spring开始获取所有内容。新的主程序如下所示:
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(); }
不幸的是,现在有一个空指针异常@:user.getEvents()。size();。(在daoImpl中)。
我知道它不应该为null(无论是从控制台的输出还是从数据库布局)。
这是控制台的更多信息(我检查过user.getEvent()== null并显示“ EVENT is NULL”):
新用户... hibernate:插入用户(名称)值(?) hibernate:插入用户(名称)值(?) hibernate:插入事件(名称,用户ID)值(?,?) hibernate:插入事件(名称,用户ID)值(?,?) hibernate:插入事件(名称,用户ID)值(?,?) 列出用户: hibernate:从用户user0_中选择user0_.id作为id0_,选择user0_.name作为name0_ 1:用户1 2:用户2 列出事件: hibernate:从事件event0_中选择event0_.id作为id1_,event0_.name作为name1_,event0_.user_id作为user3_1_ 1:User1的1:Birthday1 2:1的User1的生日2 3:为2:User2结婚 hibernate:从用户user0_中选择user0_.id作为id0_,选择user0_.name作为name0_ 用户活动 1:用户1- EVENT为NULL 2:用户2- EVENT为NULL
您可以从http://www.gargan.org/code/hibernate- test1.tgz获得示例项目(这是一个eclipse / maven项目)
解决方案(适用于控制台应用程序)
对于此问题,实际上有两种解决方案-取决于您的环境:
对于控制台应用程序,您需要一个事务模板来捕获实际的db逻辑并处理事务:
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; } }
您可以通过以下方式使用此功能:
TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate"); UserGetTransaction mt = new UserGetTransaction(context); transactionTemplate.execute(mt);
为了使它起作用,您需要为spring定义模板类(即,在basic-db.xml中):
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean>
另一个(可能的)解决方案
谢谢安迪
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); } }
解决方案(针对servlet)
Servlet并不是什么大问题。当您拥有servlet时,您可以简单地在函数的开头启动并绑定一个事务,并在结束时再次取消绑定:
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); }
我认为您不应该使用hibernate会话事务方法,而让spring这样做。
将此添加到您的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>
然后我将修改您的测试方法以使用spring事务模板:
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 } } }
附带说明一下,@OneToMany关联默认情况下是惰性的,因此您无需将其注释为惰性。(@ * ToMany默认为LAZY,@ * ToOne默认为EAGER)
编辑:从hibernate的角度来看,这就是现在正在发生的事情:
…与所有保存操作相同…
然后加载所有用户(“来自用户”查询)
那时,hibernate看到它在其会话中已经有该对象,因此丢弃从请求中获取的对象,然后从会话中返回该对象。
以下是增强代码的一些要点:
因此,要解决上述问题,可以在一个事务中进行保存,而在另一事务中进行加载:
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 } } }
或设置双方:
... event1.setUser(user); ... event2.setUser(user); ... user.setEvents(Arrays.asList(event1,event2)); ...
(也别忘了解决上面的代码增强点,“不设置列表,集合键入”)