Как сериализовать JSON, содержащий ленивые ассоциации
У меня есть Person
сущность, которая имеет @ManyToOne
Ассоциация с Contact
сущность с типом выборки LAZY. Я использую Spring-Boot для предоставления REST API. Один из моих вызовов POST содержит вложенный JSON для сохранения родительской сущности Person
вместе с ассоциацией Contact
поскольку Contact
тип извлечения LAZY, я сталкиваюсь со следующим исключением
ERROR 17415 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.example.rest.RestResultObject["results"]->java.util.ArrayList[0]->com.example.model.Person["contact"]->com.example.model.Contact_$$_jvst8d1_4["handler"])] with root cause
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.example.rest.RestResultObject["results"]->java.util.ArrayList[0]->com.example.model.Person["contact"]->com.example.model.Contact_$$_jvst8d1_4["handler"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.9.3.jar:2.9.3]
Не меняя контакт на EAGER. Есть ли лучший способ решить эту проблему?
Обновлено:
Person.java
public class Person {
private long id;
private String name;
private String rno;
@ManyToOne(fetch = FetchType.LAZY)
private Contact contact;
// Getters and setters
}
Contact.java
public class Contact {
private long id;
private String info;
@OneToMany
private List<Person> persons;
}
3 ответа
Я добавил следующие вещи
- @Entity для каждого объекта
- Chnage long to Long in id, это поможет вам, если вы используете данные весны jpa
- добавьте @Id, чтобы объявить id первичным ключом
- @JsonBackReference & @JsonManagedReference, чтобы избежать бесконечного цикла от jacson
Персональный класс
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String rno;
@JsonManagedReference
@ManyToOne(fetch = FetchType.LAZY)
private Contact contact;
//setter & getter
}
Контактный класс
@Entity
public class Contact {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String info;
@JsonBackReference
@OneToMany(cascade = CascadeType.ALL, mappedBy = "contact")
private List<Person> persons;
//setter & getter
}
и добавить зависимость
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
и, наконец, добавить новый конфиг
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization() {
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
jacksonObjectMapperBuilder.modules(new Hibernate5Module());
}
};
}
}
This is the one way to solve the problem by using
@JsonIgnore
annotation along with fethType.LAZY
public class Person {
private long id;
private String name;
private String rno;
@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnore
private Contact contact;
// Getters and setters
}
public class Contact {
private long id;
private String info;
@OneToMany
private List<Person> persons;
}
Проблема, скорее всего, связана с обработкой транзакций. Сериализация JSON выполняется вне области транзакции. Если это так, то самое простое решение (не обязательно лучшее с архитектурной точки зрения) состоит в том, чтобы создать службу, которая переносит загрузку объекта (выделенную для операций REST) и выполняет "делазификацию" связанных данных, например (ключевым элементом является @Transactional
эта заметка).
@Sevice
@Transactional(readOnly=true)
public class DataReaderServiceImpl extends DataLoaderService{
//initialization code
public Person loadPerson(PredicatesType somePredicate){
Person person = //get person using predicates expression
//"delazy" contacts in transaction scope
person.getContacts();
return person;
}
}
С архитектурной точки зрения было бы лучше, если бы в таком сервисе отображались DTO и возвращались экземпляры DTO вместо сущностей.