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

412

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

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

Все, что я нашел, это эта тема, которая в основном завершается рекомендацией избегать двунаправленных ассоциаций. У кого-нибудь есть идея для обхода этой весенней ошибки?

------ РЕДАКТИРОВАТЬ 2010-07-24 16:26:22 -------

CodeSnippets:

Бизнес-объект 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();
    }
}

persistence.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это самое чистое решение. Проверьте ответ Zammel AlaaEddine для более подробной информации.
Утку
Как вина этой весны?
Натан Хьюз
stackoverflow.com/a/53124476/3878948
Ришабх Агарвал

Ответы:

294

Вы можете использовать, @JsonIgnoreчтобы разорвать цикл.

axtavt
источник
1
@Ben: На самом деле я не знаю. Возможно, его поддержка не была включена: wiki.fasterxml.com/JacksonJAXBAnnotations
axtavt
40
Начиная с Jackson 1.6, есть лучшее решение: вы можете использовать две новые аннотации для решения проблемы бесконечной рекурсии, не игнорируя геттеры / сеттеры во время сериализации. Смотрите мой ответ ниже для деталей.
Курт Бурбаки
1
@axtavt Спасибо за идеальный ответ. Кстати, я пришел с другим решением: вы можете просто избежать создания getter для этого значения, поэтому Spring не сможет получить к нему доступ при создании JSON (но я не думаю, что это подходит для каждого случая, поэтому ваш ответ лучше)
Семен Данилов
11
Все вышеперечисленные решения кажутся необходимыми для изменения объектов домена путем добавления аннотаций. Если я сериализую сторонние классы, у меня нет возможности их изменить. Как я могу избежать этой проблемы?
Цзяньвэнь Чен
2
это решение не работает в некоторых ситуациях. В реляционной базе данных с jpa, если @JsonIgnoreвы введете значение «внешний ключ», то при обновлении сущности вы получите нулевое значение ...
slim
629

JsonIgnoreProperties [обновление 2017 года]:

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

(Спасибо As Zammel AlaaEddine за указание на это).


JsonManagedReference и JsonBackReference

Начиная с Jackson 1.6 вы можете использовать две аннотации для решения проблемы бесконечной рекурсии, не игнорируя геттеры / сеттеры во время сериализации: @JsonManagedReferenceи @JsonBackReference.

объяснение

Чтобы Джексон работал хорошо, не следует сериализовать одну из двух сторон отношения, чтобы избежать бесконечного цикла, который вызывает ошибку стека переполнения.

Итак, Джексон берет переднюю часть ссылки (вашу 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 в Json и Jackson Stackoverflow в своем блоге.

РЕДАКТИРОВАТЬ:

Другой полезной аннотацией, которую вы можете проверить, является @JsonIdentityInfo : используя ее, каждый раз, когда Джексон сериализует ваш объект, он добавляет к нему идентификатор (или другой выбранный вами атрибут), чтобы он не полностью «сканировал» его снова каждый раз. Это может быть полезно, если у вас есть цепочка между более взаимосвязанными объектами (например: Order -> OrderLine -> User -> Order и снова).

В этом случае вы должны быть осторожны, так как вам может потребоваться прочитать атрибуты вашего объекта более одного раза (например, в списке продуктов с большим количеством продуктов, имеющих одного и того же продавца), и эта аннотация не позволяет вам сделать это. Я предлагаю всегда просматривать журналы Firebug, чтобы проверить ответ Json и посмотреть, что происходит в вашем коде.

Источники:

Курт Бурбаки
источник
29
Спасибо за четкий ответ. Это более удобное решение, чем @JsonIgnoreобратная ссылка.
Утку Оздемир,
3
Это определенно правильный способ сделать это. Если вы делаете это так на стороне сервера, потому что вы используете Джексона там, то не имеет значения, какой json mapper вы используете на стороне клиента, и вам не нужно устанавливать ссылку на дочернюю родительскую ссылку. Это просто работает. Спасибо Курт
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

tero17
источник
@tero - При таком подходе мы также не получаем данные, связанные с сущностью.
Pra_A
@PAA HEY PAA я думаю, что это связано с сущностью! почему ты сказал это ?
tero17
1
@ tero17 как вы справляетесь с бесконечной рекурсией, когда у вас более двух классов? Например: Класс A -> Класс B -> Класс C -> Класс А. Я попытался с JsonIgnoreProperties без удачи
Villat
@Villat, это еще одна проблема, которую нужно решить, я предлагаю открыть новый спрос на это.
tero17
+1 для примера кода, как новичок Джексона, использование @JsonIgnoreProperties не совсем понятно, читая JavaDoc
Вехеровский
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 Я добавил подробности @JsonIdentityInfoв моем ответе выше.
Курт Бурбаки
1
это лучшее решение, которое мы нашли на данный момент, потому что когда мы использовали "@JsonManagedReference", метод get успешно возвращал значения без какой-либо ошибки stackoverflow. Но когда мы попытались сохранить данные, используя сообщение, он вернул ошибку 415 (неподдерживаемая ошибка носителя)
cuser
1
Я добавил @JsonIdentityInfoаннотацию к своим сущностям, но она не решает проблему рекурсии. Только @JsonBackReferenceи @JsonManagedReferenceрешает, но они удаляют сопоставленные свойства из JSON.
Олег Абражаев
19

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

По состоянию на июль 2011 года существует также « jackson-module-hibernate », который может помочь в некоторых аспектах работы с объектами Hibernate, хотя не обязательно именно этот (который требует аннотаций).

StaxMan
источник
Ссылки мертвы, не могли бы вы обновить их или отредактировать свой ответ.
GetMeARemoteJob
9

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

@ManyToOne
@JoinColumn(name = "ID", nullable = false, updatable = false)
@JsonIgnore
private Member member;
Манжунатх BR
источник
2
Я думаю, что @JsonIgnoreигнорирует этот атрибут от получения на стороне клиента. Что если мне нужен этот атрибут с его дочерним элементом (если у него есть дочерний элемент)?
Хасан 24-7
6

Теперь есть модуль Jackson (для Jackson 2), специально разработанный для решения проблем отложенной инициализации Hibernate при сериализации.

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>

и затем зарегистрируйте модуль при инициализации ObjectMapper Джексона:

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

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

Шейн
источник
То, что проблема делает, это решить тогда, потому что это выглядит интересно. У меня та же проблема, что и у ОП, и все уловки, в том числе и выше, не сработали.
user1567291
6

У меня хорошо получается Решить проблему бесконечной рекурсии Json при работе с Джексоном

Это то, что я сделал в OneToMany и ManyToOne Mapping.

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

и измените Test, чтобы сериализатор использовал View:

@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, он проверяет, что это не аннотировано, и игнорирует сериализацию. У вас также есть мощное и гибкое решение для выбора данных, которые вы хотите отправить через API REST.

С помощью 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;
fabioresner
источник
1
Это хорошая статья объяснить многие альтернативные решения для решения рекурсии: baeldung.com/...
Hugo Baés
5

@JsonIgnoreProperties является ответом.

Используйте что-то вроде этого:

@OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
@JsonIgnoreProperties("course")
private Set<Student> students;
ifelse.codes
источник
Используйте это с уверенностью, как я уже видел, 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;
Klapsa2503
источник
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);
Sveteek
источник
4

Вы можете использовать @JsonIgnore , но при этом будут игнорироваться данные json, к которым можно получить доступ из-за отношения внешнего ключа. Поэтому, если вы запрашиваете данные внешнего ключа (большую часть времени мы требуем), то @JsonIgnore вам не поможет. В такой ситуации следуйте приведенному ниже решению.

вы получаете бесконечную рекурсию, из-за BodyStat класса снова со ссылкой на Trainee объект

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;

Поэтому вы должны оставить комментарий / опустить вышеупомянутую часть в Trainee

Радж Кашван
источник
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;
Shubham
источник
Если я положу @ jsonIgnore аннотацию на ребенка. Я не мог получить родительский объект от ребенка. Когда я пытаюсь забрать ребенка. почему родительский объект не приходит, он игнорируется @ jsonignore. скажите мне, как добраться от ребенка к родителю и от родителя к ребенку.
Кумаресан Перумал
Не нужно использовать @JsonIgnore, просто используйте приведенные выше аннотации и для получения объектов родительского и дочернего объектов с использованием методов получения и установки. и Jsonignore тоже делает то же самое, но это создаст бесконечную рекурсию. Если Вы поделитесь своим кодом, тогда я могу проверить, почему Вы не получаете объекты. Потому что для меня оба идут.
Shubham
Я хотел сказать. при приеме родителя. Родитель должен прийти с дочерним объектом. при взятии дочернего объекта. Ребенок должен прийти с родителем. Это не работает в этом сценарии. не могли бы вы мне помочь?
Кумаресан Перумал
1

Вы можете использовать DTO шаблон создания класса TraineeDTO без какой-либо аннотации hiberbnate, и вы можете использовать Джексон маппер для преобразования Trainee в TraineeDTO и удаления сообщения об ошибке :)

Дахар Юсеф
источник
1

Если вы не можете игнорировать свойство, попробуйте изменить видимость поля. В нашем случае у нас был старый код, по-прежнему отправляющий сущности со связями, поэтому в моем случае это было исправлением:

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Trainee trainee;
Скотт Лангеберг
источник
Если я положу @ jsonIgnore аннотацию на ребенка. Я не мог получить родительский объект от ребенка. Когда я пытаюсь забрать ребенка. почему родительский объект не приходит, он игнорируется @ jsonignore. скажите мне, как добраться от ребенка к родителю и от родителя к ребенку.
Кумаресан Перумал
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.Final , поэтому, когда мне нужно чтобы показать обе сущности в представлении, я делаю следующее.

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

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

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

OJVM
источник
0

Если вы используете Spring Data Rest, проблему можно решить, создав репозитории для каждой сущности, вовлеченной в циклические ссылки.

Morik
источник
0

Я опоздал, и это уже такая длинная тема. Но я потратил пару часов, пытаясь понять это, и хотел бы привести мой случай в качестве другого примера.

Я попробовал оба решения: JsonIgnore, JsonIgnoreProperties и BackReference, но, как ни странно, казалось, что они не были подобраны.

Я использовал Lombok и подумал, что, возможно, он мешает, поскольку он создает конструкторы и переопределяет toString (видел toString в стеке stackoverflowerror).

Наконец, это была не ошибка Ломбока - я использовал автоматическую генерацию сущностей JPA NetBeans из таблиц базы данных, не задумываясь об этом - ну, и одна из аннотаций, которые были добавлены к сгенерированным классам, была @XmlRootElement. Как только я удалил его, все начало работать. Ну что ж.

hello_earth
источник
0

Дело в том, чтобы разместить @JsonIgnore в том, в метод сеттера следующим образом. в моем случае.

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;
zawhtut
источник