Нескінченна рекурсія з проблемою Джексона JSON та Hibernate JPA


412

Коли я намагаюся перетворити об'єкт JPA, який має двонаправлену асоціацію, в JSON, я продовжую отримувати

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

Все, що я знайшов - це нитка, яка в основному завершується рекомендацією уникати двонаправлених асоціацій. Хтось має ідею вирішити цю весняну помилку?

------ EDIT 2010-07-24 16:26:22 -------

Кодексипепти:

Бізнес-об’єкт 1:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "name", nullable = true)
    private String name;

    @Column(name = "surname", nullable = true)
    private String surname;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<BodyStat> bodyStats;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<Training> trainings;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<ExerciseType> exerciseTypes;

    public Trainee() {
        super();
    }

    ... getters/setters ...

Бізнес-об’єкт 2:

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "height", nullable = true)
    private Float height;

    @Column(name = "measuretime", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date measureTime;

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name="trainee_fk")
    private Trainee trainee;

Контролер:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Controller
@RequestMapping(value = "/trainees")
public class TraineesController {

    final Logger logger = LoggerFactory.getLogger(TraineesController.class);

    private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();

    @Autowired
    private ITraineeDAO traineeDAO;

    /**
     * Return json repres. of all trainees
     */
    @RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
    @ResponseBody        
    public Collection getAllTrainees() {
        Collection allTrainees = this.traineeDAO.getAll();

        this.logger.debug("A total of " + allTrainees.size() + "  trainees was read from db");

        return allTrainees;
    }    
}

JPA-реалізація стажиста DAO:

@Repository
@Transactional
public class TraineeDAO implements ITraineeDAO {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public Trainee save(Trainee trainee) {
        em.persist(trainee);
        return trainee;
    }

    @Transactional(readOnly = true)
    public Collection getAll() {
        return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
    }
}

стійкість.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0">
    <persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="validate"/>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/>         -->
        </properties>
    </persistence-unit>
</persistence>

Додати @Transientв Trainee.bodyStats.
phil294

Станом на 2017 рік, @JsonIgnorePropertiesце найчистіше рішення. Ознайомтеся з відповіддю Заммель АлааЕддін для отримання більш детальної інформації.
Утку

Як винна ця весна ??
Натан Х'юз

Відповіді:


293

Ви можете використовувати, @JsonIgnoreщоб перервати цикл.


1
@Ben: Насправді я не знаю. Можливо, його підтримка не була увімкнена: wiki.fasterxml.com/JacksonJAXBAnnotations
axtavt

40
Оскільки у Jackson 1.6 є краще рішення: ви можете використовувати дві нові анотації для вирішення нескінченної проблеми рекурсії без ігнорування геттерів / сетерів під час серіалізації. Детальну інформацію див. У моїй відповіді нижче.
Курт Бурбакі

1
@axtavt Дякую за ідеальну відповідь. До речі, я придумав інше рішення: ви можете просто уникнути створення getter для цього значення, тому Spring не зможе отримати доступ до нього під час створення JSON (але я не думаю, що він підходить для кожного випадку, тому ваша відповідь краще)
Семен Данилов

11
Усі вищезазначені рішення, здається, потребують зміни об’єктів домену, додаючи анотації. Якщо я серіалізую сторонні класи, які я не можу їх змінити. Як я можу уникнути цього питання?
Jianwu Chen

2
це рішення не працює в деяких ситуаціях. У реляційній базі даних з jpa, якщо ви покладете, @JsonIgnoreви будете нульовим "іноземним ключем", коли ви оновлюєте сутність ...
slim

628

JsonIgnoreProperties [Оновлення 2017 року]:

Тепер ви можете використовувати JsonIgnoreProperties для придушення серіалізації властивостей (під час серіалізації) або ігнорувати обробку прочитаних властивостей JSON (під час десеріалізації) . Якщо ви не шукаєте цього, будь ласка, читайте нижче.

(Дякуємо як Заммель АлааЕддін за те, що вказав на це).


JsonManagedReference та JsonBackReference

З Jackson 1.6 ви можете використовувати два анотації для вирішення нескінченної проблеми рекурсії без ігнорування геттерів / сеттерів під час серіалізації: @JsonManagedReferenceі @JsonBackReference.

Пояснення

Щоб Джексон добре працював, одна з двох сторін відносин не повинна бути серіалізована, щоб уникнути нескінченного циклу, який викликає вашу помилку stackoverflow.

Отже, Джексон приймає пряму частину посилання (ваш Set<BodyStat> bodyStatsу класі Trainee) і перетворює її у формат сховища json; це так званий процес марширування . Потім Джексон шукає задню частину посилання (тобто Trainee traineeв класі BodyStat) і залишає її такою, якою вона є, не серіалізуючи її. Ця частина взаємозв'язку буде перебудована під час десеріалізації ( демарширування ) прямої посилання.

Ви можете змінити свій код так (я пропускаю непотрібні частини):

Бізнес-об’єкт 1:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    @JsonManagedReference
    private Set<BodyStat> bodyStats;

Бізнес-об’єкт 2:

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name="trainee_fk")
    @JsonBackReference
    private Trainee trainee;

Тепер все повинно працювати належним чином.

Якщо ви хочете отримати більше інформації, я написав статтю про проблеми Джсона та Джексона Стакковерфла про Keenformatics , мій блог.

Редагувати:

Ще одна корисна примітка, яку ви можете перевірити, - це @JsonIdentityInfo : використовуючи її, кожен раз, коли Джексон серіалізує ваш об’єкт, він додасть до нього ідентифікатор (або інший атрибут, який ви обрали), щоб він не повністю повністю сканував його кожен раз. Це може бути корисно, якщо у вас є ланцюговий цикл між більш взаємопов'язаними об'єктами (наприклад: Order -> OrderLine -> User -> Order і знову).

У цьому випадку ви повинні бути обережними, оскільки вам може знадобитися читати атрибути вашого об'єкта не один раз (наприклад, у списку товарів з більшою кількістю товарів, що мають одного продавця), і ця анотація заважає вам це робити. Я пропоную завжди заглянути в журнали firebug, щоб перевірити відповідь Json і побачити, що відбувається у вашому коді.

Джерела:


29
Дякую за чітку відповідь. Це більш зручне рішення, ніж @JsonIgnoreзвернення до зворотного посилання.
Utku Özdemir

3
Це, безумовно, правильний спосіб зробити це. Якщо ви робите це так на сервері, тому що ви використовуєте Джексона там, не має значення, який картограф json ви використовуєте на стороні клієнта, і вам не доведеться встановлювати дитину в посібник з батьківських посилань. Це просто працює. Дякую Курту
flosk8

1
Приємне, детальне пояснення і, безумовно, кращий і більш описовий підхід, ніж @JsonIgnore.
Пьотр Новицький

2
Дякую! @JsonIdentityInfo працював для циклічних посилань, які включали кілька сутностей у багатьох петлях, що перекриваються.
n00b

1
Я не можу змусити це працювати на все життя. Я думаю, у мене досить схожа настройка, але я, очевидно, щось помилився, оскільки не можу отримати нічого, крім нескінченних помилок рекурсії:
swv

103

Нова анотація @JsonIgnoreProperties вирішує багато питань з іншими параметрами.

@Entity

public class Material{
   ...    
   @JsonIgnoreProperties("costMaterials")
   private List<Supplier> costSuppliers = new ArrayList<>();
   ...
}

@Entity
public class Supplier{
   ...
   @JsonIgnoreProperties("costSuppliers")
   private List<Material> costMaterials = new ArrayList<>();
   ....
}

Перевірте це тут. Це працює так само, як у документації:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html


@tero - І при такому підході ми не отримуємо даних, пов’язаних із сутністю.
Pra_A

@PAA HEY PAA Я думаю, що це асоціюється з сутністю! чому ти так кажеш ?
tero17

1
@ tero17 як ти керуєш нескінченною рекурсією, коли у тебе більше 2 класів? Наприклад: Клас А -> Клас В -> Клас С -> Клас А. Я без спроби намагався з JsonIgnoreProperties
Villat

@Villat це ще одна проблема, яку потрібно вирішити, я пропоную відкрити новий попит на це.
tero17

+1 для зразка коду, як новачок Джексона, використання @JsonIgnoreProperties було не зовсім зрозуміло, прочитавши JavaDoc
Wecherowski

47

Також за допомогою Jackson 2.0+ ви можете використовувати @JsonIdentityInfo. Це працювало набагато краще для моїх сплячих занять, ніж @JsonBackReferenceта @JsonManagedReference, які мали для мене проблеми і не вирішували питання. Просто додайте щось на зразок:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@traineeId")
public class Trainee extends BusinessObject {

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@bodyStatId")
public class BodyStat extends BusinessObject {

і це має працювати.


Можете пояснити, будь ласка, "Це працювало набагато краще"? Чи є проблема з керованою довідкою?
Utku Özdemir

@ UtkuÖzdemir Я додав деталі про @JsonIdentityInfoсвою відповідь вище.
Курт Бурбакі

1
це найкраще рішення, яке ми знайшли поки що, оскільки коли ми використовували "@JsonManagedReference" метод get успішно повертав значення без будь-якої помилки stackoverflow. Але, коли ми намагалися зберегти дані за допомогою публікації, вона повернула помилку 415 (непідтримувана медіа-помилка)
cuser

1
Я додав @JsonIdentityInfoанотацію до моїх організацій, але це не вирішує проблему рекурсії. Тільки @JsonBackReferenceі@JsonManagedReference вирішує, але вони видаляють відображені властивості з JSON.
Олег Абражаєв

19

Крім того, у Jackson 1.6 є підтримка для обробки двонаправлених посилань ... що схоже на те, що ви шукаєте ( ця запис у блозі також згадує про цю функцію)

І станом на липень 2011 року, також існує " jackson-module-hibernate ", який може допомогти в деяких аспектах роботи зі об'єктами сплячки, хоча це не обов'язково саме цей (для чого потрібні анотації).


Посилання мертві, чи не проти оновлювати їх чи редагувати свою відповідь.
GetMeARemoteJob


9

Це для мене спрацювало чудово. Додайте примітку @JsonIgnore до дочірнього класу, де ви згадуєте посилання на батьківський клас.

@ManyToOne
@JoinColumn(name = "ID", nullable = false, updatable = false)
@JsonIgnore
private Member member;

2
Я думаю, що @JsonIgnoreігнорує цей атрибут від отримання до клієнта. Що робити, якщо мені потрібен цей атрибут з його дитиною (якщо він має дитину)?
Хасан 24-7

6

Зараз є модуль Джексона (для Jackson 2), спеціально розроблений для вирішення проблем із ініціалізацією лінивого сплячого режиму при серіалізації.

https://github.com/FasterXML/jackson-datatype-hibernate

Просто додайте залежність (зауважте, що для Hibernate 3 та Hibernate 4 існують різні залежності):

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-hibernate4</artifactId>
  <version>2.4.0</version>
</dependency>

а потім зареєструйте модуль під час інтиляції Jackson's ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());

Документація в даний час не велика. Дивіться код Hibernate4Module щодо доступних опцій.


Те, що виникає, вирішується тоді, адже це виглядає цікаво. У мене те саме питання, що і в ОП, і всі хитрощі, включаючи вище, не спрацювали.
користувач1567291

6

Для мене добре вирішити проблему з нескінченною рекурсією Джсона при роботі з Джексоном

Це те, що я зробив у картографії OneToMany та ManyToOne

@ManyToOne
@JoinColumn(name="Key")
@JsonBackReference
private LgcyIsp Key;


@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;

Я використовував карти сплячого режиму у програмі весняного завантаження
Prabu M

Привіт авторе, дякую за приємні підручники та чудові пости. Однак я виявив , що @JsonManagedReference, @JsonBackReferenceне дає вам дані , пов'язані з @OneToManyі @ManyToOneсценарії, а також при використанні @JsonIgnorePropertiesж пропустити відповідні дані сутності. Як це вирішити?
Pra_A

5

Для мене найкращим рішенням є використання @JsonViewта створення конкретних фільтрів для кожного сценарію. Можна також використовувати @JsonManagedReferenceі @JsonBackReference, тим НЕ менш , це жорстко рішення лише в одному випадку, коли власник завжди посилається на що володіє сторону, і ніколи в зворотному. Якщо у вас є інший сценарій серіалізації, коли вам потрібно по-іншому повторно анотувати атрибут, ви не зможете.

Проблема

Дозволяє використовувати два класи, Companyі Employeeтам, де ви маєте циклічну залежність між ними:

public class Company {

    private Employee employee;

    public Company(Employee employee) {
        this.employee = employee;
    }

    public Employee getEmployee() {
        return employee;
    }
}

public class Employee {

    private Company company;

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }
}

І тестовий клас, який намагається серіалізувати за допомогою ObjectMapper( Spring Boot ):

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {

    @Autowired
    public ObjectMapper mapper;

    @Test
    public void shouldSaveCompany() throws JsonProcessingException {
        Employee employee = new Employee();
        Company company = new Company(employee);
        employee.setCompany(company);

        String jsonCompany = mapper.writeValueAsString(company);
        System.out.println(jsonCompany);
        assertTrue(true);
    }
}

Якщо запустити цей код, ви отримаєте:

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

Рішення за допомогою "@ JsonView"

@JsonViewдозволяє використовувати фільтри та вибирати, які поля слід включати під час серіалізації об’єктів. Фільтр - це лише посилання на клас, що використовується як ідентифікатор. Тож спочатку створимо фільтри:

public class Filter {

    public static interface EmployeeData {};

    public static interface CompanyData extends EmployeeData {};

} 

Пам'ятайте, що фільтри - це фіктивні класи, які просто використовуються для вказівки полів із @JsonViewанотацією, тож ви можете створити стільки, скільки вам потрібно і потрібно. Давайте подивимось це на дію, але спочатку нам потрібно анотувати наш Companyклас:

public class Company {

    @JsonView(Filter.CompanyData.class)
    private Employee employee;

    public Company(Employee employee) {
        this.employee = employee;
    }

    public Employee getEmployee() {
        return employee;
    }
}

і змінити тест, щоб серіалізатор міг використовувати Вид:

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {

    @Autowired
    public ObjectMapper mapper;

    @Test
    public void shouldSaveCompany() throws JsonProcessingException {
        Employee employee = new Employee();
        Company company = new Company(employee);
        employee.setCompany(company);

        ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
        String jsonCompany = writter.writeValueAsString(company);

        System.out.println(jsonCompany);
        assertTrue(true);
    }
}

Тепер, якщо ви запускаєте цей код, проблема нескінченної рекурсії вирішена, оскільки ви прямо сказали, що ви просто хочете серіалізувати атрибути, котрі були помічені @JsonView(Filter.CompanyData.class) .

Коли він доходить до зворотного посилання компанії в компанії Employee, він перевіряє, чи не зазначено, і ігнорує серіалізацію. У вас також є потужне і гнучке рішення для вибору даних, які ви хочете надсилати через свої REST API.

Завдяки Spring ви можете коментувати свої методи REST Controllers за допомогою потрібного @JsonViewфільтра, а серіалізація прозоро застосовується до об'єкта, що повертається.

Ось імпорт, який використовується, якщо вам потрібно перевірити:

import static org.junit.Assert.assertTrue;

import javax.transaction.Transactional;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

import com.fasterxml.jackson.annotation.JsonView;

1
Це приємна стаття, що пояснює багато альтернативних рішень для вирішення рекурсій: baeldung.com/…
Hugo Baés

5

@JsonIgnoreProperties - це відповідь.

Використовуйте щось подібне ::

@OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
@JsonIgnoreProperties("course")
private Set<Student> students;

Використовуйте це впевнено, оскільки я бачив, що Jhipster використовує це у створеному коді
ifelse.codes

Дякую за відповідь. Однак я виявив , що @JsonManagedReference, @JsonBackReferenceне дає вам дані , пов'язані з @OneToManyі @ManyToOneсценарії, а також при використанні @JsonIgnorePropertiesж пропустити відповідні дані сутності. Як це вирішити?
Pra_A

4

У моєму випадку достатньо було змінити відношення з:

@OneToMany(mappedBy = "county")
private List<Town> towns;

до:

@OneToMany
private List<Town> towns;

інше відношення залишилося таким, яким воно було:

@ManyToOne
@JoinColumn(name = "county_id")
private County county;

2
Я думаю, що краще використовувати рішення Курта. Тому що рішення JoinColumn може закінчитися нереференційними даними мертвих тіл.
flosk8

1
Це насправді єдине, що мені допомогло. Інші рішення зверху не працювали. Я досі не впевнений, чому ...
Деніс М.

4

Обов'язково використовуйте com.fasterxml.jackson скрізь. Я витратив багато часу, щоб дізнатися це.

<properties>
  <fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${fasterxml.jackson.version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${fasterxml.jackson.version}</version>
</dependency>

Потім використовуйте @JsonManagedReferenceі @JsonBackReference.

Нарешті, ви можете серіалізувати свою модель до JSON:

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);

4

Ви можете використовувати @JsonIgnore , але це буде ігнорувати дані json, до яких можна отримати доступ через зв’язок із зовнішнім ключем. Тому, якщо ви перенаправляєте дані іноземних ключів (більшість потрібного нам часу), то @JsonIgnore вам не допоможе. У такій ситуації, будь ласка, дотримуйтесь наведеного нижче рішення.

Ви отримуєте нескінченну рекурсію, оскільки клас BodyStat знову посилається на об’єкт « Стажист»

BodyStat

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="trainee_fk")
private Trainee trainee;

Стажер

@OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Column(nullable = true)
private Set<BodyStat> bodyStats;

Тому ви повинні коментувати / опускати вищевказану частину в Стажері


1
У моєму випадку це не працює. Чи можете ви подивіться, будь ласка: github.com/JavaHelper/issue-jackson-boot ?
Pra_A

3

Я також зустрічався з тією ж проблемою. Я використовував @JsonIdentityInfo«s ObjectIdGenerators.PropertyGenerator.classтип генератора.

Це моє рішення:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...

2
Гарна відповідь
ЛАГРІДА

1
Я відповів би те саме! Ніцца;)
Феліпе Десідерати

3

Вам слід використовувати @JsonBackReference з об'єктом @ManyToOne та @JsonManagedReference з класами сутностей, що містять @onetomany.

@OneToMany(
            mappedBy = "queue_group",fetch = FetchType.LAZY,
            cascade = CascadeType.ALL
        )
    @JsonManagedReference
    private Set<Queue> queues;



@ManyToOne(cascade=CascadeType.ALL)
        @JoinColumn(name = "qid")
       // @JsonIgnore
        @JsonBackReference
        private Queue_group queue_group;

Якщо я поставив @ jsonIgnore анотацію у дитини. Я не міг отримати батьківський предмет від дитини. Коли я намагаюся взяти дитину. чому батьківський об’єкт не надходить, він ігнорується @ jsonignore. підкажіть, як дістатися від дитини до батька і батька до дитини.
Kumaresan Perumal

Не потрібно використовувати @JsonIgnore, просто використовуйте наведені вище анотації та Для отримання об’єктів батьків та дитини, використовуючи Getters та setters. і Jsonignore теж робить те саме, але це створить нескінченну рекурсію. Якщо ви ділитесь своїм кодом, то я можу перевірити, чому ви не отримуєте об'єктів. Тому що для мене обидва йдуть.
Shubham

Я мав на увазі сказати. при прийнятті батьків. Батько повинен прийти з дочірнім предметом. при взятті дочірнього предмета. Дитина повинна прийти з батьком. Це не працює в цьому сценарії. Ви можете мені допомогти?
Kumaresan Perumal

1

ви можете використовувати шаблон DTO для створення класу TraineeDTO без будь-якого анотації в сплячому режимі, і ви можете використовувати картограф Джексона, щоб перетворити стажиста в TraineeDTO і бінго повідомлення про помилку не працює :)


1

Якщо ви не можете ігнорувати властивість, спробуйте змінити видимість поля. У нашому випадку у нас був старий код, який все ще подає суб'єкти, які мають відносини, тож у моєму випадку це виправлення:

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Trainee trainee;

Якщо я поставив @ jsonIgnore анотацію у дитини. Я не міг отримати батьківський предмет від дитини. Коли я намагаюся взяти дитину. чому батьківський об’єкт не надходить, він ігнорується @ jsonignore. підкажіть, як дістатися від дитини до батька і батька до дитини.
Kumaresan Perumal

0

У мене була ця проблема, але я не хотів використовувати анотацію в своїх об'єктах, тому я вирішив, створивши конструктор для свого класу, цей конструктор не повинен мати посилання на суб'єкти, які посилаються на цю сутність. Скажімо, такий сценарій.

public class A{
   private int id;
   private String code;
   private String name;
   private List<B> bs;
}

public class B{
   private int id;
   private String code;
   private String name;
   private A a;
}

Якщо ви спробуєте переслати клас перегляду Bабо Aз @ResponseBodyним, це може спричинити нескінченний цикл. Ви можете написати конструктор у своєму класі та створити запит із entityManagerподібним.

"select new A(id, code, name) from A"

Це клас з конструктором.

public class A{
   private int id;
   private String code;
   private String name;
   private List<B> bs;

   public A(){
   }

   public A(int id, String code, String name){
      this.id = id;
      this.code = code;
      this.name = name;
   }

}

Однак щодо цього рішення є деякі обмеження, як ви бачите, в конструкторі я не робив посилання на List bs, це тому, що Hibernate не дозволяє цього, принаймні у версії 3.6.10.Закінчення , тому коли мені потрібно щоб показати обидва об'єкти у вигляді, я виконую наступне.

public A getAById(int id); //THE A id

public List<B> getBsByAId(int idA); //the A id.

Інша проблема цього рішення полягає в тому, що якщо ви додаєте або видалите властивість, ви повинні оновити конструктор і всі ваші запити.


0

Якщо ви використовуєте Spring Data Rest, проблему можна вирішити, створивши сховища для кожного суб'єкта, що бере участь у циклічних посиланнях.


0

Я пізній учасник, і це вже така довга нитка. Але я витратив пару годин, намагаючись розібратися і в цьому, і хотів би навести свою справу як інший приклад.

Я спробував рішення JsonIgnore, JsonIgnoreProperties та BackReference, але як не дивно, це було так, ніби їх не взяли.

Я використовував Lombok і думав, що, можливо, це заважає, оскільки він створює конструктори і переосмислює toString (побачив toString у стеці stackoverflowerror).

Нарешті, це не вина Ломбока - я використовував автоматичне створення NetBeans об'єктів JPA з таблиць баз даних, не замислюючись над цим - ну, і одне з анотацій, додане до створених класів, було @XmlRootElement. Як тільки я його зняв, все почало працювати. Ну добре.


0

Сенс полягає в тому, щоб розмістити @JsonIgnore в методі setter наступним чином. в моєму випадку

Township.java

@Access(AccessType.PROPERTY)
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
    return villages;
}

@JsonIgnore
@Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
    this.villages = villages;
}

Village.java

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;

@Column(name = "townshipId", nullable=false)
Long townshipId;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.