我有两个实体,它们处于多对多关系。
@Entity
public class Room {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToMany(mappedBy = "rooms")
private Set<Team> teams;
}
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToMany
@JoinTable(name = "teams_rooms",
joinColumns = @JoinColumn(name= "team_id"),
inverseJoinColumns = @JoinColumn(name = "room_id"))
private Set<Room> rooms;
}
为了生成数据,我有一个“房间”和“团队”的存储库:
public interface RoomRepository extends CrudRepository<Room, Long> {
}
public interface TeamRepository extends CrudRepository<Team, Long> {
}
我的目标是请求一个团队的所有房间,但防止JPA无限循环。
@RestController
@RequestMapping("....")
public class RoomController {
@Autowired
private RoomRepository roomRepository;
@GetMapping
public Iterable<Room> getAllRoomsOfTeam() {
final long exampleId = 1; //This is just a placeholder. The id will be passed as a parameter.
final var team = teamRepository.findById(exampleId);
return ResponseEntity.ok(team);
}
}
这是结果:
{
"id": 1,
"name": "Team1",
"rooms": [
{
"id": 1,
"name": "Room 1",
"teams": [
{
"id": 1,
"name": "Team 1",
"rooms": [
{
"id": 1,
"name": "Room 1",
"teams": [
Jackson 将永远循环,直到发生异常(因为反向引用也引用父元素,这将创建一个循环)。 我已经尝试过
@JsonManagedReference
和 @JsonBackReference
,但它们用于多对一关系。
如何阻止杰克逊无限循环?我希望尽可能少地影响其他存储库和查询。
您的控制器不应返回entities(带有注释@Entity的类)。最佳实践是创建另一个具有相同属性的单独类。这段代码有一点重复,但它使所有层保持干净。我还建议使用@Service。
public class RoomDTO {
private String name;
private List<TeamDTO> teams = new ArrayList<>();
public RoomDTO() {
}
public RoomDTO(Room room) {
this.name = room.name;
for(Team team : room.getTeams()) {
TeamDTO teamDTO = new TeamDTO();
teamDTO.setName(team.getName);
teams.add(teamDTO);
}
}
}
public class TeamDTO {
List<RoomDTO> rooms = new ArrayList();
public TeamDTO() {
}
public TeamDTO(Team team) {
this.name = team.name;
for(Room room : team.getRooms()) {
RoomDTO roomDTO = new RoomDTO();
roomDTO.setName(team.getName);
rooms.add(roomDTO);
}
}
}
控制器应该返回这个
@GetMapping
public Iterable<TeamDTO> getAllRoomsOfTeam() {
final long exampleId = 1;
final var team = teamRepository.findById(exampleId);
TeamDTO teamDTO = new TeamDTO(team);
return ResponseEntity.ok(teamDTO);
}
目前,您的类中存在循环依赖关系,这会在将对象转换为
JSON
时导致问题。请在 @JsonIgnore
类中的 rooms
变量上添加 Team
注释,如下例所示:
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToMany
@JoinTable(name = "teams_rooms",
joinColumns = @JoinColumn(name= "team_id"),
inverseJoinColumns = @JoinColumn(name = "room_id"))
@JsonIgnore
private Set<Room> rooms;
}
如果您需要双向转换的解决方案,那么您可以使用
JsonView
注释。
首先,您需要为
Team
和 Room
创建 JSON 视图配置文件,如下例所示:
public class JsonViewProfiles
{
/**
* This profile will be used while converting Team object to JSON
*/
public static class Team {}
/**
* This profile will be used while converting Room object to JSON
*/
public static class Room {}
}
在您的实体中使用上面创建的 JSON 视图配置文件,如下例所示:
public class Room {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonView({ JsonViewProfiles.Team.class, JsonViewProfiles.Room.class })
private long id;
@JsonView(JsonViewProfiles.Room.class)
@ManyToMany(mappedBy = "rooms")
private Set<Team> teams;
}
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonView({JsonViewProfiles.Team.class, JsonViewProfiles.Room.class})
private long id;
@ManyToMany
@JoinTable(name = "teams_rooms",
joinColumns = @JoinColumn(name= "team_id"),
inverseJoinColumns = @JoinColumn(name = "room_id"))
@JsonView(JsonViewProfiles.Team.class)
private Set<Room> rooms;
}
将对象转换为 JSON 时,请使用这些配置文件,如下例所示:
@GetMapping
public String getAllRoomsOfTeam() {
final long exampleId = 1; //This is just a placeholder. The id will be passed as a parameter.
final Team team = teamRepository.findById(exampleId);
String result = new ObjectMapper().writerWithView(JsonViewProfiles.Team.class)
.writeValueAsString(team);
return result;
}
对我来说,结果是@JsonIgnoreProperties
public class Venta {
@JsonIgnoreProperties("venta_producto")
@ManyToMany()
@JoinTable(
name="venta_producto",
joinColumns=
@JoinColumn(name="venta_id", referencedColumnName="codigo_venta"),
inverseJoinColumns=
@JoinColumn(name="codigo_id", referencedColumnName="codigo_producto")
)
private List<Producto> listaProductos;
@JsonIgnoreProperties("venta")
@ManyToOne()
@JoinColumn(name = "id_cliente")
private Cliente unCliente;
}
public class Producto {
@JsonIgnoreProperties("producto")
@ManyToMany(mappedBy="listaProductos")
private List<Venta> listVenta;
}
public class Cliente {
@JsonIgnoreProperties("unCliente")
@OneToMany(mappedBy="unCliente")
private List<Venta> listVenta;
}