Ошибка отложенной загрузки в сериализаторе JSON - PullRequest
4 голосов
/ 08 июня 2011

У меня есть такой вид @OneToOne Hibernate relationsShip

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

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

К сожалению, это не работает из-за отложенных предложений, и мне не удается:

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-файл.

Ответы [ 5 ]

6 голосов
/ 08 июня 2011

Самое простое решение: не сериализуйте сущности, используйте объекты-значения.

Если это не вариант для вас, убедитесь, что объект сущности отсоединен.

В JPA (2) вы должны использовать EntityManager.detach(entity), а в обычном Hibernate эквивалент будет Session.evict(entity).

5 голосов
/ 01 февраля 2012

Когда-то я написал процессор, чтобы справиться с этим, но теперь это легко исправить, используя модуль спящего режима Джексона .

4 голосов
/ 08 июня 2011

В вашем методе DAO добавьте Hibernate.initialize(<your getter method>);, чтобы решить эту проблему.

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

Попробуйте как описано выше.

0 голосов
/ 12 ноября 2017

Но как я могу сериализовать ленивые классы JSON с использованием стандартного Джексона? конвертер сообщений, причину которого я добавил в свой XML-файл.

Прежде всего, я не советую использовать DTO / Value Object только для решения этой проблемы.
В начале вам может показаться, что это легко, но при каждой новой разработке / изменении дублирующий код означает, что каждый раз нужно вносить две модификации ... в противном случае ошибки.

Я не имею в виду, что VO или DTO - это неприятные запахи, но вы должны использовать их по причинам, для которых они предназначены (например, для предоставления контента / структуры, которые различаются в зависимости от логических уровней или решения неразрешимой проблемы сериализации).
Если у вас есть чистый и эффективный способ решения проблемы сериализации без VO / DTO и они вам не нужны, не используйте их.

И об этом, есть много способов решить проблему отложенной загрузки, когда вы используете Джексона с Hibernate сущностями.

На самом деле самый простой способ - это использовать FasterXML / jackson-datatype-hibernate

Проект по созданию модуля Джексона (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.version.datatype должно быть одинаковым для используемой версии Джексона и расширения типа данных ackson.

Если вы используете или можете использовать Spring Boot, вам просто нужно объявить модуль как bean-компонент в определенном классе Configuration или в классе SpringBootApplication, и он будет автоматически зарегистрирован для любого созданного Jackson ObjectMapper.

В разделе 74.3 Customize the Jackson ObjectMapper Spring Boot указано, что:

Любые бобы типа com.fasterxml.jackson.databind.Module будут автоматически регистрируется в автоматически настроенном 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();
    }
}
0 голосов
/ 26 января 2012

Есть еще один вариант, который решает ваши проблемы. Вы можете добавить этот фильтр в 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>

Проблема в том, что объекты загружаются лениво, и сериализация происходит до того, как они будут загружены полностью.

...