Бесконечная рекурсия с выпуском 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 ответов
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 и посмотреть, что происходит в вашем коде.
Источники:
- Keenformatics - Как решить бесконечную рекурсию JSON Stackru (мой блог)
- Джексон Ссылки
- Личный опыт
Новая аннотация @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. Как только я удалил, все заработало. Ну что ж.