JSON序列化程序中的延迟加载错误

时间:2011-06-08 10:37:08

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

我有这种@OneToOne Hibernate relationShip

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的关系将解决它。

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

5 个答案:

答案 0 :(得分:6)

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

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

使用JPA(2),您可以使用EntityManager.detach(entity),使用普通的Hibernate,等效的是Session.evict(entity)

答案 1 :(得分:5)

一旦我编写处理器来处理这个问题,但现在可以使用jackson hibernate module轻松解决此问题。

答案 2 :(得分:4)

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

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

尝试上述方法。

答案 3 :(得分:0)

还有另一种选择可以解决您的问题。您可以在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>

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

答案 4 :(得分:0)

  

但是如何使用标准的jackson序列化为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特性相关的明确定义的扩展集。

要做到这一点,你只需要添加所需的依赖项并添加 杰克逊模块可在需要的处理过程中使用。

如果您使用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版本和ackson-datatype扩展名的jackson.version.datatype应该相同。

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

74.3 Customize the Jackson ObjectMapper Spring Boot部分声明:

  

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

例如:

ObjectMapper

或:

@Configuration
public class MyJacksonConfig {

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