Spring Boot多对多关联实体加载异常:查询时作者信息丢失
多对多关联查询时作者信息随机不显示问题
我定义了BookEntity与AuthorEntity两个实体,二者为多对多关联关系。通过Postman创建记录时结果符合预期,但调用getBooks或getBookById查询时,会随机出现作者信息不显示的异常。
实体类代码
@Data @Entity @NoArgsConstructor @AllArgsConstructor @Builder @Table(name = "books") public class BookEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable( name = "books_authors", joinColumns = {@JoinColumn(name = "book_id")}, inverseJoinColumns = {@JoinColumn(name = "author_id")} ) private Set<AuthorEntity> authors; }
@Data @Entity @NoArgsConstructor @AllArgsConstructor @Builder @Table(name = "authors") public class AuthorEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private Integer age; private Boolean dead; @ManyToMany(mappedBy = "authors", fetch = FetchType.EAGER) @JsonBackReference private Set<BookEntity> books; }
控制器与服务层代码
Book控制器代码
@PostMapping public ResponseEntity<BookDTO> createBook(@RequestBody BookDTO bookDTO){ BookEntity bookEntity; Set<AuthorEntity> authors = utilities.authorInsideBook(bookDTO); if(authors == null){ return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } bookEntity = bookMapper.mapFrom(bookDTO); bookEntity.setAuthors(authors); bookEntity = bookServiceImpl.save(bookEntity); return new ResponseEntity<>(bookMapper.mapTo(bookEntity), HttpStatus.OK); } @GetMapping public List<BookDTO> getBooks(){ List<BookEntity> books = bookServiceImpl.getBooks(); return books.stream().map(bookMapper::mapTo).toList(); } @GetMapping(path = "/{id}") public ResponseEntity<BookDTO> getBookById(@PathVariable(value = "id") Long id){ Optional<BookEntity> bookEntity = bookServiceImpl.getBookById(id); return bookEntity.map(book -> { BookDTO bookDTO = bookMapper.mapTo(book); return new ResponseEntity<>(bookDTO, HttpStatus.FOUND); }).orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @DeleteMapping(path = "/{id}") public ResponseEntity<HttpStatus> deleteBooksById(@PathVariable(value = "id") Long id){ if(!bookServiceImpl.isExists(id)){ return new ResponseEntity<>(HttpStatus.NOT_FOUND); } bookServiceImpl.delete(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); }
Book服务层代码
@Override public BookEntity save(BookEntity bookEntity) { return bookRepository.save(bookEntity); } @Override public List<BookEntity> getBooks() { return StreamSupport.stream( bookRepository.findAll() .spliterator(), false) .toList(); } @Override public Optional<BookEntity> getBookById(Long id) { return bookRepository.findById(id); } @Override public boolean isExists(Long id) { return bookRepository.existsById(id); } @Override public void delete(Long id) { bookRepository.deleteById(id); }
问题原因与解决方案
核心原因
- 双向多对多均设置
FetchType.EAGER:Hibernate在双向EAGER加载多对多关系时,易出现加载顺序冲突或循环加载问题,导致关联集合随机无法初始化。 - 集合序列化与加载上下文问题:
Set集合的无序性加上EAGER加载的序列化上下文波动,可能导致JSON序列化时随机忽略关联数据。
解决方案
1. 调整FetchType并主动关联查询
将实体类中的FetchType.EAGER改为FetchType.LAZY,同时在Repository中通过JOIN FETCH主动加载关联数据,避免延迟加载导致的未初始化问题:
- 修改
BookEntity的多对多注解:
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinTable( name = "books_authors", joinColumns = {@JoinColumn(name = "book_id")}, inverseJoinColumns = {@JoinColumn(name = "author_id")} ) private Set<AuthorEntity> authors;
- 修改
AuthorEntity的多对多注解:
@ManyToMany(mappedBy = "authors", fetch = FetchType.LAZY) @JsonBackReference private Set<BookEntity> books;
- 在
BookRepository中添加带关联查询的方法:
@Query("SELECT b FROM BookEntity b JOIN FETCH b.authors") List<BookEntity> findAllWithAuthors(); @Query("SELECT b FROM BookEntity b JOIN FETCH b.authors WHERE b.id = :id") Optional<BookEntity> findByIdWithAuthors(Long id);
- 服务层调用新的Repository方法:
@Override public List<BookEntity> getBooks() { return bookRepository.findAllWithAuthors(); } @Override public Optional<BookEntity> getBookById(Long id) { return bookRepository.findByIdWithAuthors(id); }
2. 优化JSON序列化配置
在BookEntity的authors字段上添加@JsonManagedReference,与AuthorEntity的@JsonBackReference配合,明确序列化方向,避免循环序列化问题:
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinTable( name = "books_authors", joinColumns = {@JoinColumn(name = "book_id")}, inverseJoinColumns = {@JoinColumn(name = "author_id")} ) @JsonManagedReference private Set<AuthorEntity> authors;
3. 检查Mapper映射逻辑
确认bookMapper.mapTo方法正确处理authors集合的映射,确保即使集合为空也能正常序列化该字段,而非直接忽略。
内容的提问来源于stack exchange,提问作者maziogra
相关产品推荐
相关产品推荐

