【问题标题】:Lazy Loading error延迟加载错误
【发布时间】:2011-05-20 11:55:03
【问题描述】:

我知道这个问题已经在这里提出过好几次了。但是我找不到任何有用的解决方案,所以我决定再次发布。

我正在使用带有 Spring MVC 框架的 Hibernate,我正在尝试使用子类获取我的类:

public class Scene implements Serializable{
private Long id;
private Author author;
//omitting getters and setters
} 

public class Author{
private Long id;
Private String  name; 
//omitting getters and setters
}

我的 Hibernate 配置如下:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="org.postgresql.Driver" />
  <property name="url" value="jdbc:postgresql://127.0.0.1/doolloop2" />
  <property name="username" value="doolloop2" />
  <property name="password" value="doolloop" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
     <property name="mappingLocations">
      <list>
        <value>WEB-INF/mapping/Scene.hbm.xml</value>
        <value>WEB-INF/mapping/Author.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
 <props>
  <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop> 
  <prop key="hibernate.show_sql">true</prop> 
  <prop key="hibernate.hbm2ddl.auto">update</prop> 
  <prop key="hibernate.cache.use_second_level_cache">true</prop> 
  <prop key="hibernate.cache.use_query_cache">true</prop> 
  <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
  </props>
  </property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="txService" class="com.doolloop.services.TxDataBaseServiceImpl">
        <property name="sessionFactory">
        <ref local="sessionFactory"/>
        </property>
    </bean> 

现在我有了获取场景的方法:

@Override
    @Transactional(readOnly = true)
    public Scene getScene(Long id) {
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createQuery("select scene from com.doolloop.objects.Scene scene where scene.id=:id");
        q.setLong("id", id);
        Scene scene = (Scene)q.uniqueResult()
        return scene;

    }

还有我的spring mvc代码:

 @RequestMapping(value="/getSceneMetaData.dlp",method = RequestMethod.GET)
     @ResponseBody
     public Scene getSceneMetaData(@RequestParam String id){
        Scene scene = service.getScene(Long.parseLong(id));
        return scene;
    }

当我执行它时,我得到了以下异常:

Dec 5, 2010 12:29:21 PM org.hibernate.LazyInitializationException <init>
    SEVERE: failed to lazily initialize a collection of role: com.doolloop.objects.Scene.author, no session or session was closed
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.doolloop.objects.Scene.vauthor, no session or session was closed

我用谷歌搜索它并理解它的发生是因为它会在 Transaction 关闭时尝试获取作者,并且默认情况下作者类是惰性的,但我不明白我应该如何修改我的代码 n 才能解决它。

欢迎任何建议。

UPD:这是两个类的映射文件:

<class
        name="com.doolloop.objects.Scene"
        table="scene"
    >
        <id
            name="id"
            column="Id"
            type="java.lang.Long"
            unsaved-value="null"
        >
         <generator class="sequence">
                <param name="sequence">doolloop2.sceneseq</param>
            </generator>
        </id>
    <many-to-one
         name="author"
         class="com.doolloop.objects.Author"
         cascade="all"
         column="authorid"
         unique="false"
     />  
    </class>

<hibernate-mapping>
    <class
        name="com.doolloop.objects.Author"
        table="authors"
    >
        <id
            name="id"
            column="Id"
            type="java.lang.Long"
            unsaved-value="null"
        >
         <generator class="sequence">
                <param name="sequence">doolloop2.authorseq</param>
            </generator>
        </id>
        <property
            name="name"
            update="true"
            insert="true"
            not-null="false"
            unique="false"
            type="java.lang.String"
        >
            <column name="name" />
        </property>
             <property
            name="email"
            update="true"
            insert="true"
            not-null="false"
            unique="false"
            type="java.lang.String"
        >
            <column name="email" />
        </property>
        <property
            name="site"
            update="true"
            insert="true"
            not-null="false"
            unique="false"
            type="java.lang.String"
        >
            <column name="site" />
        </property>
       <property
            name="contactInfo"
            update="true"
            insert="true"
            not-null="false"
            unique="false"
            type="java.lang.String"
        >
            <column name="contactInfo" />
        </property>
    </class>

UPD2 我在 web.xml 中添加了以下内容:

 <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/applicationContext.xml</param-value>
</context-param>
  <filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>openSessionInViewFilter</filter-name>
    <url-pattern>/getSceneMetaData.dlp</url-pattern>
</filter-mapping>

它给了我以下异常:

org.codehaus.jackson.map.JsonMappingException: could not initialize proxy - no Session (through reference chain: com.doolloop.objects.Scene["author"]->com.doolloop.objects.Author_$$_javassist_4["name"])

更新3:

这是此操作的 log4j 输出:

2010-12-06 11:48:09,031 DEBUG [org.springframework.orm.hibernate3.support.OpenSessionInViewFilter] - Using SessionFactory 'sessionFactory' for OpenSessionInViewFilter
2010-12-06 11:48:09,036 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'sessionFactory'
2010-12-06 11:48:09,040 DEBUG [org.springframework.orm.hibernate3.support.OpenSessionInViewFilter] - Opening single Hibernate Session in OpenSessionInViewFilter
2010-12-06 11:48:09,042 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - Opening Hibernate Session
2010-12-06 11:48:09,048 DEBUG [org.springframework.web.servlet.DispatcherServlet] - DispatcherServlet with name 'doolloop' processing GET request for [/dlp/getSceneMetaData.dlp]
2010-12-06 11:48:09,056 DEBUG [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping] - Mapping [/getSceneMetaData.dlp] to HandlerExecutionChain with handler [com.doolloop.controllers.SceneController@21c33b55] and 2 interceptors
2010-12-06 11:48:09,060 DEBUG [org.springframework.web.servlet.DispatcherServlet] - Last-Modified value for [/dlp/getSceneMetaData.dlp] is: -1
2010-12-06 11:48:09,142 DEBUG [org.springframework.core.convert.support.GenericConversionService] - Converting value '140' of [TypeDescriptor java.lang.String] to [TypeDescriptor @org.springframework.web.bind.annotation.RequestParam java.lang.String]
2010-12-06 11:48:09,146 DEBUG [org.springframework.core.convert.support.GenericConversionService] - Converted to '140'
2010-12-06 11:48:09,148 DEBUG [org.springframework.web.bind.annotation.support.HandlerMethodInvoker] - Invoking request handler method: public com.doolloop.objects.Scene com.doolloop.controllers.SceneController.getSceneMetaData(java.lang.String)
2010-12-06 11:48:16,401 DEBUG [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver] - Resolving exception from handler [com.doolloop.controllers.SceneController@21c33b55]: org.codehaus.jackson.map.JsonMappingException: could not initialize proxy - no Session (through reference chain: com.doolloop.objects.Scene["author"]->com.doolloop.objects.Author_$$_javassist_4["name"])
2010-12-06 11:48:16,561 DEBUG [org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver] - Resolving exception from handler [com.doolloop.controllers.SceneController@21c33b55]: org.codehaus.jackson.map.JsonMappingException: could not initialize proxy - no Session (through reference chain: com.doolloop.objects.Scene["author"]->com.doolloop.objects.Author_$$_javassist_4["name"])
2010-12-06 11:48:16,568 DEBUG [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] - Resolving exception from handler [com.doolloop.controllers.SceneController@21c33b55]: org.codehaus.jackson.map.JsonMappingException: could not initialize proxy - no Session (through reference chain: com.doolloop.objects.Scene["author"]->com.doolloop.objects.Author_$$_javassist_4["name"])
2010-12-06 11:48:16,572 DEBUG [org.springframework.web.servlet.DispatcherServlet] - Could not complete request
org.codehaus.jackson.map.JsonMappingException: could not initialize proxy - no Session (through reference chain: com.doolloop.objects.Scene["author"]->com.doolloop.objects.Author_$$_javassist_4["name"])
    at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:215)

//// Entire stack trace goes here.

2010-12-06 11:48:16,660 DEBUG [org.springframework.orm.hibernate3.support.OpenSessionInViewFilter] - Closing single Hibernate Session in OpenSessionInViewFilter
2010-12-06 11:48:16,662 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closing Hibernate Session

更新4:

我只看到一个选择 n 控制台:

Hibernate: select scene0_.Id as Id20_0_, scene0_.name as name20_0_, scene0_.description as descript3_20_0_, scene0_.defaultView as defaultV4_20_0_, scene0_.authorid as authorid20_0_ from scene scene0_ where scene0_.Id=?

但是,如果我这样修改我的 getScene 方法:

 public Scene getScene(Long id) {
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createQuery("select scene from com.doolloop.objects.Scene scene where scene.id=:id");
        q.setLong("id", id);
        Scene scene = (Scene)q.uniqueResult()
        **Author author = scene.getAuthor();**
        return scene; 

我会看到第二个选择出现:

Hibernate: select author0_.Id as Id23_0_, author0_.name as name23_0_, author0_.email as email23_0_, author0_.site as site23_0_, author0_.contactInfo as contactI5_23_0_ from authors author0_ where author0_.Id=?

但作者对象仍为空。

从控制器调用相同的 getter 会导致异常。

【问题讨论】:

    标签: hibernate transactions jakarta-ee spring-mvc lazy-loading


    【解决方案1】:

    可能值得先发布 Scene.hbm.xml 和 Author.hbm.xml..

    从我在这里看到的: https://forum.hibernate.org/viewtopic.php?f=1&t=994772&start=0 如果你使用注解,多对一关系会默认加载,但是如果你使用 XML 映射就不会发生这种情况!!

    我会说修改多对一关系并明确设置fetch="EAGER"

    所以结果应该是:

    <many-to-one
         name="author"
         class="com.doolloop.objects.Author"
         cascade="all"
         column="authorid"
         unique="false"
         fetch="EAGER"
     />  
    

    您提到您有两种使用场景:一种是您获取 Scene 对象并需要它的所有依赖项,另一种是您只对 id 和它的几个字段感兴趣。这似乎是定义 Hibernate Session 边界的问题。您可以使用此处描述的 SessionInViewFilter:http://community.jboss.org/wiki/OpenSessioninView 这样,Hibernate 会话将在 http 请求期间打开,因此您将能够访问延迟加载的引用来渲染时间。这有帮助吗?

    ==================================
    我已经看到了您的 OpenSessionInViewFilter 定义 - 可能值得明确设置您的 HibernateSessionFactory 的名称:

    <filter>
       <filter-name>openSessionInViewFilter</filter-name>
       <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
         <init-param>
            <param-name>sessionFactoryBeanName</param-name>
            <param-value>sessionFactory</param-value>
         </init-param>
     </filter>
    

    另外,我没有在 Spring Config 中看到 Tx Manager 的正确初始化。仅仅定义它是不够的,你需要:

    <tx:annotation-driven transaction-manager="transactionManager"/>
    

    ==================================
    从我在 Hibernate 文档中看到的内容来看,很可能一旦您关闭事务,您的 Hibernate 会话就会结束(即使您使用 OpenSessionInView 过滤器)。 看看这里: http://community.jboss.org/wiki/OpenSessioninView#Can_I_commit_the_transaction_before_rendering_the_view

    关闭事务后,数据库连接将返回到池中,并且对数据库的所有进一步访问都需要一个新连接,该连接应启用自动提交模式。

    但是,在您的情况下,我认为解决方案是确保您在事务仍处于打开状态时已加载所有需要从数据库加载的内容!

    【讨论】:

    • 完成,但我认为问题不存在,问题出在我处理交易的方式上,但无论如何提前谢谢您。
    • 首先感谢您的回复,在那之前我不知道。但是,我想保持惰性,因为我有两种情况,一种是检索场景,我需要所有项目,第二种是搜索,我只需要场景对象的 ID、名称和描述。
    • 不幸的是,它没有帮助:(
    • 所以,您使用的是 OpenSessionInViewFilter,您已经正确配置了 TxManager,但您仍然遇到此问题。您正在处理的数据是在该特定请求期间获取的,还是您从中获取的http 会话。换句话说,您确定您使用的对象仍然附加到 Hibernate 会话吗?
    • 我认为是的。请查看此操作的 log4j。在我的更新 3.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 2020-04-10
    相关资源
    最近更新 更多