Spring-boot JPA 无限循环多对多

问题描述 投票:0回答:3

我有两个实体,它们处于多对多关系。

@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
,但它们用于多对一关系。

如何阻止杰克逊无限循环?我希望尽可能少地影响其他存储库和查询。

java spring spring-boot jpa jackson
3个回答
3
投票

您的控制器不应返回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);

}

如何在控制器、服务和存储库模式中使用DTO


3
投票

目前,您的类中存在循环依赖关系,这会在将对象转换为

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;
}

0
投票

对我来说,结果是@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;
}
© www.soinside.com 2019 - 2024. All rights reserved.