Тесты Пообщаться с GPT Протестировать код
Программирование Аналитика Дизайн Маркетинг Управление проектами
24 Авг 2023
2 мин
796

Бесконечная рекурсия с Jackson JSON и Hibernate JPA

Пройдите тест, узнайте какой профессии подходите
Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Одной из распространённых проблем в Java, с которой сталкиваются разработчики, работающие с Hibernate JPA и Jackson JSON, является ошибка бесконечной рекурсии или

Одной из распространённых проблем в Java, с которой сталкиваются разработчики, работающие с Hibernate JPA и Jackson JSON, является ошибка бесконечной рекурсии или Infinite recursion. Эта проблема обычно возникает при попытке преобразовать объект JPA, имеющий двунаправленную ассоциацию, в JSON.

Ошибка бесконечной рекурсии обычно выглядит следующим образом:

1
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)

Эта проблема происходит из-за того, что при сериализации объекта с двунаправленной ассоциацией, Jackson пытается сериализовать оба объекта взаимно, что приводит к бесконечной рекурсии.

Рассмотрим пример. Предположим, у нас есть два класса Person и Car, связанные двунаправленной ассоциацией:

1
2
3
4
5
6
7
8
9
10
11
public class Person {
    private String name;
    private Car car;
    //getters and setters
}
 
public class Car {
    private String model;
    private Person owner;
    //getters and setters
}

В случае попытки сериализации объекта Person в JSON, Jackson пытается сериализовать связанный с ним объект Car. Но т.к. Car также ссылается на Person, Jackson пытается сериализовать Person еще раз и так далее.

Однако, существуют способы обойти эту проблему, не избегая использования двунаправленных ассоциаций.

Один из способов — использовать аннотации @JsonManagedReference и @JsonBackReference библиотеки Jackson:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Person {
    private String name;
 
    @JsonManagedReference
    private Car car;
    //getters and setters
}
 
public class Car {
    private String model;
 
    @JsonBackReference
    private Person owner;
    //getters and setters
}

В данном случае @JsonManagedReference указывает на то, что поле должно быть сериализовано, а @JsonBackReference указывает на то, что поле должно быть пропущено во время сериализации.

Ещё одним решением проблемы может быть использование аннотации @JsonIgnore для пропуска определенного поля при сериализации:

1
2
3
4
5
6
7
public class Car {
    private String model;
 
    @JsonIgnore
    private Person owner;
    //getters and setters
}

Таким образом, поле owner будет пропущено при сериализации объекта Car, что предотвратит возникновение бесконечной рекурсии.

Также можно использовать аннотацию @JsonIdentityInfo, которая позволяет обрабатывать циклические зависимости в данных, сохраняя идентификаторы объектов вместо их сериализации:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class Person {
    private String name;
    private Car car;
    //getters and setters
}
 
@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class Car {
    private String model;
    private Person owner;
    //getters and setters
}

В этом случае, вместо сериализации объекта Person при сериализации объекта Car будет сохранен только идентификатор Person. Это также предотвратит возникновение бесконечной рекурсии.

Добавить комментарий