【问题标题】:Lazy Loadng error in JSON serializerJSON序列化程序中的延迟加载错误
【发布时间】:2011-06-08 10:37:08
【问题描述】:

我有这种@OneToOne Hibernate 关系

public class Address implements Serializable {

    private String id;
    private String city;
    private String country;
//setter getters ommitted
}

public class Student implements Serializable {

    private String id;
    private String firstName;
    private String lastName;    
    private Address address;
}

地址项被映射为 LAZY。

现在我想使用

获取用户及其地址
session.load(Student.class,id);

在我的 daoService 中。

然后我将它作为 JSON 从我的 Spring MVC 控制器返回:

@RequestMapping(value="/getStudent.do",method=RequestMethod.POST)
    @ResponseBody
    public Student getStudent(@RequestParam("studentId") String id){
        Student student = daoService.getStudent(id);
        return student;
    }

不幸的是,由于 Lazy clasees,它无法正常工作,我失败了:

org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.vanilla.objects.Student_$$_javassist_1["address"]->com.vanilla.objects.Address_$$_javassist_0["handler"])
    at org.codehaus.jackson.map.ser.StdSerializerProvider$1.serialize(StdSerializerProvider.java:62)

我确实使用 OpenSessionInViewInterceptor,它工作得很好。 我知道我可以用户离开加入 HQL 查询并以这种方式检索学生和地址并解决问题。我也明白改变与 EAGER 的关系会解决这个问题。

但是如何使用标准杰克逊消息转换器将其序列化为 JSON 惰性类,这是我添加到我的 XML 文件中的原因。

【问题讨论】:

    标签: json hibernate spring spring-mvc lazy-loading


    【解决方案1】:

    最简单的解决方案:不要序列化实体,使用值对象。

    如果这不适合您,请确保实体对象已分离。

    对于 JPA (2),您将使用 EntityManager.detach(entity),对于普通 Hibernate,等效于 Session.evict(entity)

    【讨论】:

    • 不幸的是,驱逐没有帮助。它与所有代理类一样分离对象,然后我因 ​​org.hibernate.LazyInitializationException 而失败,因为该对象没有会话。
    • @danny 这就是我所害怕的,但值得一试。然后使用值对象:-)
    • 您能解释一下值对象是什么意思吗? :)
    • @SeanPatrickFloyd 这应该如何解决使用 spring data jpa 时的问题,您没有直接访问 entitymanager 的权限?
    • 再次@ProgMania,使用值对象
    【解决方案2】:

    曾经我编写了一个处理器来处理这个问题,但现在使用jackson hibernate module 很容易解决这个问题。

    【讨论】:

      【解决方案3】:

      在您的 DAO 方法中添加 Hibernate.initialize(<your getter method>); 以解决此问题。

      Student student = findById(<yourId>);
      Hibernate.initialize(student.getAddress());
      ...
      return student;
      

      像上面那样试试。

      【讨论】:

      • 对一个 getter 有好处,但如果我有 60 个 -70 个 getter 或 sub-getter 就不好了。
      • 如果您有将近 60-70 个相关的 entity getter 或 setter,这可能表明设计非常糟糕。答案是假设您只遇到了一些问题。
      【解决方案4】:

      还有另一个选项可以解决您的问题。您可以在 web.xml 中添加此过滤器

      <filter>
          <filter-name>springOpenEntityManagerInViewFilter</filter-name>
          <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
          <init-param>
            <param-name>entityManagerFactoryBeanName</param-name>
            <param-value>entityManagerFactory</param-value>
          </init-param>
        </filter>
        <filter-mapping>
          <filter-name>springOpenEntityManagerInViewFilter</filter-name>
          <url-pattern>/*</url-pattern>
        </filter-mapping>
      

      问题是实体被延迟加载,并且序列化发生在它们完全加载之前。

      【讨论】:

      • 我没有使用 EntityManager,我正在使用会话。
      【解决方案5】:

      但是如何使用标准杰克逊序列化为 JSON 惰性类 消息转换器是我添加到我的 XML 文件中的原因。

      首先,我不建议使用 DTO/Value Object 来解决此问题。
      一开始您可能会发现它很容易,但是在每次新的开发/更改时,重复的代码意味着每次都要进行两次修改……否则会出现错误。

      我并不是说 VO 或 DTO 是难闻的气味,但您应该出于设计原因(例如提供根据逻辑层不同的内容/结构或解决无法解决的序列化问题)来使用它们。
      如果您有一种干净有效的方法来解决没有 VO/DTO 的序列化问题并且您不需要它们,请不要使用它们。

      关于它,当您将 Jackson 与 Hibernate 实体一起使用时,有很多方法可以解决延迟加载问题。

      其实最简单的方法就是使用FasterXML/jackson-datatype-hibernate

      构建Jackson模块(jar)以支持JSON序列化的项目 和特定于 Hibernate (http://hibernate.org) 的反序列化 数据类型和属性;尤其是延迟加载方面。

      它提供Hibernate3Module/Hibernate4Module/Hibernate5Module,扩展模块可以注册到ObjectMapper,以提供一组定义良好的与Hibernate 特性相关的扩展。

      要让它工作,你只需要添加所需的依赖项并添加 Jackson 模块在需要的处理过程中可用。

      如果你使用 Hibernate 3:

        <dependency>
           <groupId>com.fasterxml.jackson.datatype</groupId>
           <artifactId>jackson-datatype-hibernate3</artifactId>
           <version>${jackson.version.datatype}</version>
        </dependency>
      

      如果你使用 Hibernate 4:

        <dependency>
           <groupId>com.fasterxml.jackson.datatype</groupId>
           <artifactId>jackson-datatype-hibernate4</artifactId>
           <version>${jackson.version.datatype}</version>
        </dependency>
      

      所以……

      其中 jackson.version.datatype 对于使用的 Jackson 版本和 ackson-datatype 扩展名应该相同。

      如果您使用或可能使用 Spring Boot,您只需将模块声明为特定 Configuration 类或 SpringBootApplication 类中的 bean,它将自动为任何创建的 Jackson ObjectMapper 注册。

      74.3 Customize the Jackson ObjectMapper Spring Boot 部分指出:

      com.fasterxml.jackson.databind.Module 类型的任何 bean 都将 使用自动配置自动注册 Jackson2ObjectMapperBuilder 并应用于任何 ObjectMapper 实例 它创造的。这提供了一个全球性的贡献机制 向应用程序添加新功能时的自定义模块。

      例如:

      @Configuration
      public class MyJacksonConfig {
      
          @Bean
          public Module hibernate5Module() {
            return new Hibernate5Module();
          }
      }
      

      或:

      @SpringBootApplication
      public class AppConfig {
      
          public static void main(String[] args) throws IOException {
            SpringApplication.run(AppConfig.class, args);
          }
      
          @Bean
          public Module hibernate5Module() {
            return new Hibernate5Module();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-20
        • 1970-01-01
        相关资源
        最近更新 更多