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

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

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

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

------ РЕДАКТИРОВАНИЕ 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>

29 ответов

Решение

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

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

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

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


JsonManagedReference и JsonBackReference

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

объяснение

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

Итак, Джексон берет на себя переднюю часть ссылки (ваш Set<BodyStat> bodyStats в классе стажера) и преобразует его в 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 Stackru в своем блоге.

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

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

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

Источники:

Новая аннотация @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

Также с помощью 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 {

и это должно работать.

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

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

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

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

Теперь Джексон поддерживает избегание циклов без игнорирования полей:

Джексон - сериализация сущностей с двунаправленными отношениями (избегая циклов)

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

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

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


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

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

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

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

Для меня лучшим решением является использование @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 (StackruError)

Решение с использованием `@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;

Теперь есть модуль 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 для доступных опций.

Вы должны использовать @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;

ОЧЕНЬ ВАЖНО: если вы используете LOMBOK, убедитесь, что вы исключили атрибуты коллекций, такие как Set, List и т. Д.

Как это:

@EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})

В моем случае было достаточно изменить отношение с:

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

чтобы:

@OneToMany
private List<Town> towns;

другое отношение осталось прежним:

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

Я также встретил ту же проблему. я использовал @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 {
...

Вы можете использовать @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

Убедитесь, что вы используете 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);

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

@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)
@JsonBackReference
private Set<BodyStat> bodyStats;

Если вы используете @JsonManagedReference, @JsonBackReference или же @JsonIgnore аннотация игнорирует некоторые поля и решает бесконечную рекурсию с помощью Jackson JSON.

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

      @JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="@id")

Обратитесь к этой статье https://www.toptal.com/javascript/bidirectional-relationship-in-json, чтобы получить хорошее представление о @JsonIdentityInfo аннотация.

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

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

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

По какой-то причине в моем случае он не работал с Set. Мне пришлось изменить его на List и использовать @JsonIgnore и @ToString.Exclude, чтобы заставить его работать.

Заменить набор списком:

//before
@OneToMany(mappedBy="client")
private Set<address> addressess;

//after
@OneToMany(mappedBy="client")
private List<address> addressess;

И добавьте аннотации @JsonIgnore и @ToString.Exclude:

@ManyToOne
@JoinColumn(name="client_id", nullable = false)
@JsonIgnore
@ToString.Exclude
private Client client;

В этом сообщении: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion есть полное объяснение.

Если вы используете Jackson со старыми версиями, вы можете попробовать @jsonmanagedreference + @jsonbackreference. Если ваш Джексон выше 2 (1.9 также не работает, насколько я знаю), попробуйте вместо этого @JsonIdentityInfo.

Как человек, использующий Spring Data и Lombok, я решил это для себя.

      @Entity
@Data
public class Foo extends BaseEntity {

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "foo_id")
    @JsonIgnoreProperties("parent_foo")
    @EqualsAndHashCode.Exclude
    private Set<Bar> linkedBars;
}

@Entity
@Data
public class Bar extends BaseEntity {

    @Column(name = "foo_id")
    private Long parentFooId;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "foo_id", insertable = false, updatable = false)
    @JsonIgnoreProperties({"linkedBars"})
    private Foo parentFoo;
}

The JsonIgnorePropertiesаннотация останавливает бесконечную рекурсию, как обсуждалось во многих ответах выше.

@EqualsAndHashCode.ExcludeпредотвращаетStackOverflowErrorвызванныйhashCodeиequalsвызывается рекурсивно.

С использованиемSetнадListрешаетMultipleBagFetchExceptionчто происходит, когда вы добавляете несколько полей коллекции. Вы также можете использовать@Fetch(value = FetchMode.SUBSELECT)чтобы избежать декартова продукта, но я не пробовал его лично, так как мой вариант использования не нуждался в этом.

Явное определениеparentFooIdв том, чтобы разрешить сопоставлениеFooюридические лица сBarс.

Я столкнулся с той же проблемой, добавьте jsonbackref и jsonmanagedref и убедитесь, что методы @override равно и hashCode, это определенно решит эту проблему.

Дело в том, чтобы поместить @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;

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

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

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.

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

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

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

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

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

Другие вопросы по тегам