[英]Returning a lazy loaded entity in the JSON response
我的Club
实体有问题 - 我正在使用LAZY
获取类型和ModelMapper
返回我的 JSON。问题是,如果我使用LAZY
而不是EAGER
,我得到的响应是GET
/api/players/{id}
是:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not initialize proxy
以及来自 Postman 的屏幕截图:
当我调试控制器的动作时:
@GetMapping("/api/players/{id}")
ResponseEntity<PlayerDto> getPlayer(@PathVariable String id) {
Player foundPlayer = playerInterface.getPlayer(Long.valueOf(id));
PlayerDto playerToDto = convertToDto(foundPlayer);
return ResponseEntity.ok().body(playerToDto);
}
...
private PlayerDto convertToDto(Player player) {
return modelMapper.map(player, PlayerDto.class);
}
似乎foundPlayer
和playerToDto
都有这样的Club
:
但是当我执行foundPlayer.getClub().getName()
时,我得到了一个正确的名称。 我知道这可能是预期的行为,但我希望Club
像这样在我的回复中返回(如果设置了EAGER
,则为回复的屏幕截图):
无需将获取类型设置为EAGER
。
我的Player
实体:
@Entity
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;;
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.EAGER)
@JsonManagedReference
private Club club;
我的Club
实体:
@Entity
public class Club {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "club", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JsonBackReference
private List<Player> players;
来自PlayerService
的getPlayer
方法(controller 调用的那个方法):
@Override
public Player getPlayer(Long id) {
Optional<Player> foundPlayer = playerRepository.findById(id);
return foundPlayer.orElseThrow(PlayerNotFoundException::new);
}
PlayerToDto
:
package pl.ug.kchelstowski.ap.lab06.dto;
import pl.ug.kchelstowski.ap.lab06.domain.Club;
public class PlayerDto {
private Long id;
private String firstName;
private String lastName;
private Club club;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Club getClub() {
return club;
}
public void setClub(Club club) {
this.club = club;
}
}
没错,这是延迟加载的预期行为。 这是一件好事,不要将其设置为 eager,而不是直接在您的响应主体上返回一个 Club @Entity class。 您应该创建一个 ClubDto 并使用另一个 convertToDto 方法对其进行初始化。 这有点乏味(我喜欢使用Mapstruct和Lombok来缓解这种情况),但它会诱导 Hibernate 进行您需要的所有查询。
@Data
public class ClubDto {
private String id;
private String name;
}
@Mapper
public interface ClubMapper {
public ClubDTO mapToDto(Club club);
}
糟糕,没有意识到您已经在使用ModelMapper 。 我对此不太熟悉,但听起来如果您将 Club 换成 ClubDto 就可以了。
伙计们,我有一个解决方案,但我想听听您是否可以通过这种方式完成,或者它是某种反模式。 我只是简单地将playerToDto
的 Club 设置为带有foundPlayer
ID 的全新获取的Club
@GetMapping("/api/players/{id}")
ResponseEntity<PlayerDto> getPlayer(@PathVariable String id) {
Player foundPlayer = playerInterface.getPlayer(Long.valueOf(id));
PlayerDto playerToDto = convertToDto(foundPlayer);
playerToDto.setClub(clubInterface.getClub(foundPlayer.getClub().getId()));
return ResponseEntity.ok().body(playerToDto);
}
最后我想到了这个:
@GetMapping("/api/players")
ResponseEntity<List<PlayerDto>> getAllPlayers() {
List<PlayerDto> playersList = playerInterface.getAllPlayers().stream().map(this::convertToDto).collect(Collectors.toList());
playersList.forEach(playerInterface::fetchClubToPlayer);
return ResponseEntity.ok().body(playersList);
}
@GetMapping("/api/players/{id}")
ResponseEntity<PlayerDto> getPlayer(@PathVariable String id) {
Player foundPlayer = playerInterface.getPlayer(Long.valueOf(id));
PlayerDto playerToDto = convertToDto(foundPlayer);
playerInterface.fetchClubToPlayer(playerToDto);
return ResponseEntity.ok().body(playerToDto);
}
public PlayerDto fetchClubToPlayer(PlayerDto player) {
if (player.getClub() != null) {
Club club = clubInterface.getClub(player.getClub().getId());
player.setClub(club);
}
return player;
}
还好吗?
我建议您使用@EntityGraph
来配置结果方法查询的获取计划。 例如,您可以在PlayerRepository
中声明一个方法来通过 id 查找Player
实体,除了默认的findById
方法,它的Club
实体将被急切地获取。
public interface PlayerRepository extends JpaRepository<Player, Long>{
...
@EntityGraph(attributePaths = {"club"})
Optional<Player> findWithClubFetchedEagerlyById(LongId);
}
通过提供attributePaths
,定义了应该急切获取的字段。
如果在调用findById
方法时总是急切地获取Club
实体,则不需要单独的方法,因此您可以使用@EntityGraph
注释默认方法。
有了这个解决方案,.network 遍历被最小化,因为所有需要的数据都是从数据库中一次获取的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.