#json #spring #spring-boot #jpa
Вопрос:
Привет, у меня есть приведенный ниже код, который возвращает все книги и соответствующую информацию об издателе. Но я вижу только первый результат, в первом и третьем объектах просто есть записи книгоиздателей, но во 2-м объекте нет вложенного объекта.
Что-то не так с сериализацией JPA или JSON? Нужно ли что-то изменить для целей сериализации?
Решения Пробовали:
Я пытался удалить аннотацию JsonIdentityInfo как из книги, так и из издателя, но это приводит к бесконечной рекурсии.
Код:
@Data
@NoArgsConstructor
@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id",
scope = Book.class)
public class Book implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BookPublisher> bookPublishers = new HashSet<>();
public Book(String name) {
this.name = name;
}
}
@Data
@NoArgsConstructor
@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id",
scope = Publisher.class)
public class Publisher implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
@OneToMany(mappedBy = "publisher")
private Set<BookPublisher> bookPublishers = new HashSet<>();
public Publisher(String name) {
this.name = name;
}
}
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "book_publisher")
@AssociationOverrides({
@AssociationOverride(name = "id.bookId", joinColumns = @JoinColumn(name = "book_id")),
@AssociationOverride(name = "id.publisherId",
joinColumns = @JoinColumn(name = "publisher_id"))})
public class BookPublisher implements Serializable {
@EmbeddedId
private BookPublisherId id;
@ManyToOne
@MapsId("bookId")
@JoinColumn(name = "book_id")
private Book book;
@ManyToOne
@MapsId("publisherId")
@JoinColumn(name = "publisher_id")
private Publisher publisher;
@Column(name = "published_date")
private Date publishedDate;
public BookPublisher(Book book, Publisher publisher, Date publishedDate) {
this.id = new BookPublisherId(book.getId(), publisher.getId());
this.book = book;
this.publisher = publisher;
this.publishedDate = publishedDate;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Embeddable
public class BookPublisherId implements Serializable {
@Column(name = "book_id")
private Integer bookId;
@Column(name = "publisher_id")
private Integer publisherId;
}
Ожидаемый:
[
{
"id": 1,
"name": "Book1",
"bookPublishers": [
{
"publisher": {
"id": 2,
"name": "Pub2"
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
},
{
"publisher": {
"id": 1,
"name": "Pub1"
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
}
]
},
{
"id": 2,
"name": "Book2",
"bookPublishers": [
{
"publisher": {
"id": 1,
"name": "Pub1"
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
},
{
"publisher": {
"id": 2,
"name": "Pub2"
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
}
]
},
{
"id": 3,
"name": "Book3",
"bookPublishers": [
{
"publisher": {
"id": 3,
"name": "Pub3"
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
}
]
}
]
Actual:
[
{
"id": 1,
"name": "Book1",
"bookPublishers": [
{
"id": {
"bookId": 1,
"publisherId": 2
},
"book": 1,
"publisher": {
"id": 2,
"name": "Pub2",
"bookPublishers": [
{
"bookId": 1,
"publisherId": 2
},
{
"id": {
"bookId": 2,
"publisherId": 2
},
"book": {
"id": 2,
"name": "Book2",
"bookPublishers": [
{
"id": {
"bookId": 2,
"publisherId": 1
},
"book": 2,
"publisher": {
"id": 1,
"name": "Pub1",
"bookPublishers": [
{
"bookId": 2,
"publisherId": 1
},
{
"id": {
"bookId": 1,
"publisherId": 1
},
"book": 1,
"publisher": 1,
"publishedDate": "2021-06-06T14:42:30.754 00:00"
}
]
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
},
{
"bookId": 2,
"publisherId": 2
}
]
},
"publisher": 2,
"publishedDate": "2021-06-06T14:42:30.754 00:00"
}
]
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
},
{
"bookId": 1,
"publisherId": 1
}
]
},
2, <<<<<<<<<<<<<- Here whole object itself missing???
{
"id": 3,
"name": "Book3",
"bookPublishers": [
{
"id": {
"bookId": 3,
"publisherId": 3
},
"book": 3,
"publisher": {
"id": 3,
"name": "Pub3",
"bookPublishers": [
{
"bookId": 3,
"publisherId": 3
}
]
},
"publishedDate": "2021-06-06T14:42:30.754 00:00"
}
]
}
]
DB Results:
select * from book;
select * from publisher;
select * from book_publisher;
id name
1 Book1
2 Book2
3 Book3
id name
1 Pub1
2 Pub2
3 Pub3
book_id publisher_id published_date
1 1 2021-06-06 20:12:30.7540000
1 2 2021-06-06 20:12:30.7540000
2 1 2021-06-06 20:12:30.7540000
2 2 2021-06-06 20:12:30.7540000
3 3 2021-06-06 20:12:30.7540000
Solutions Tried But still same Problem: Added equals/hashcode for all class
Please find the implementations for each class:
Book:
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Book Book = (Book) o;
return Objects.equals(name, Book.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
Publisher:
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Publisher Publisher = (Publisher) o;
return Objects.equals(name, Publisher.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
BookPublisher:
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
BookPublisher that = (BookPublisher) o;
return Objects.equals(book, that.book) amp;amp; Objects.equals(publisher, that.publisher);
}
@Override
public int hashCode() {
return Objects.hash(book, publisher);
}
BookPublisherId:
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
BookPublisherId that = (BookPublisherId) o;
return Objects.equals(bookId, that.bookId) amp;amp; Objects.equals(publisherId, that.publisherId);
}
@Override
public int hashCode() {
return Objects.hash(bookId, publisherId);
}
Выходной запрос, сгенерированный для BookRepository.findAll():
Hibernate: select book0_.id as id1_0_, book0_.name as name2_0_ from Book book0_
Hibernate: select bookpublis0_.book_id as book_id1_1_0_, bookpublis0_.publisher_id as publishe2_1_0_, bookpublis0_.book_id as book_id1_1_1_, bookpublis0_.publisher_id as publishe2_1_1_, bookpublis0_.published_date as publishe3_1_1_, publisher1_.id as id1_3_2_, publisher1_.name as name2_3_2_ from book_publisher bookpublis0_ inner join Publisher publisher1_ on bookpublis0_.publisher_id=publisher1_.id where bookpublis0_.book_id=?
Hibernate: select bookpublis0_.publisher_id as publishe2_1_0_, bookpublis0_.book_id as book_id1_1_0_, bookpublis0_.book_id as book_id1_1_1_, bookpublis0_.publisher_id as publishe2_1_1_, bookpublis0_.published_date as publishe3_1_1_, book1_.id as id1_0_2_, book1_.name as name2_0_2_ from book_publisher bookpublis0_ inner join Book book1_ on bookpublis0_.book_id=book1_.id where bookpublis0_.publisher_id=?
Hibernate: select bookpublis0_.book_id as book_id1_1_0_, bookpublis0_.publisher_id as publishe2_1_0_, bookpublis0_.book_id as book_id1_1_1_, bookpublis0_.publisher_id as publishe2_1_1_, bookpublis0_.published_date as publishe3_1_1_, publisher1_.id as id1_3_2_, publisher1_.name as name2_3_2_ from book_publisher bookpublis0_ inner join Publisher publisher1_ on bookpublis0_.publisher_id=publisher1_.id where bookpublis0_.book_id=?
Hibernate: select bookpublis0_.publisher_id as publishe2_1_0_, bookpublis0_.book_id as book_id1_1_0_, bookpublis0_.book_id as book_id1_1_1_, bookpublis0_.publisher_id as publishe2_1_1_, bookpublis0_.published_date as publishe3_1_1_, book1_.id as id1_0_2_, book1_.name as name2_0_2_ from book_publisher bookpublis0_ inner join Book book1_ on bookpublis0_.book_id=book1_.id where bookpublis0_.publisher_id=?
Hibernate: select bookpublis0_.book_id as book_id1_1_0_, bookpublis0_.publisher_id as publishe2_1_0_, bookpublis0_.book_id as book_id1_1_1_, bookpublis0_.publisher_id as publishe2_1_1_, bookpublis0_.published_date as publishe3_1_1_, publisher1_.id as id1_3_2_, publisher1_.name as name2_3_2_ from book_publisher bookpublis0_ inner join Publisher publisher1_ on bookpublis0_.publisher_id=publisher1_.id where bookpublis0_.book_id=?
Hibernate: select bookpublis0_.publisher_id as publishe2_1_0_, bookpublis0_.book_id as book_id1_1_0_, bookpublis0_.book_id as book_id1_1_1_, bookpublis0_.publisher_id as publishe2_1_1_, bookpublis0_.published_date as publishe3_1_1_, book1_.id as id1_0_2_, book1_.name as name2_0_2_ from book_publisher bookpublis0_ inner join Book book1_ on bookpublis0_.book_id=book1_.id where bookpublis0_.publisher_id=?
Комментарии:
1. Все ли ваши внешние ключи присутствуют в базе данных? Это может объяснить, почему у вас есть только некоторые записи.
2. @Pilpo да, у него есть результаты, и я прикрепил результаты базы данных также для всех таблиц в вопросе.
3. Другой момент заключается в том, что вы не написали явные значения/хэш-код, которые необходимы, потому что вы используете Set<> (а также embeded id) (Hibernate нужны для вычисления хэша в соответствии с вашим объектом, а не в соответствии с идентификатором)
4. @Pilpo Добавление данных аннотаций из lambok также не помогает в «@EqualsAndHashCode»…. или мне нужно разместить ручную реализацию Equals amp; Hashcode?
5. Я недостаточно знаю Ломбок, чтобы говорить об этом. Во-первых, вы должны попытаться добавить их вручную. В случае успеха, их протестируют с Ломбоком 🙂