使用 Spring Boot 的 RESTful API 中的循环依赖

Posted

技术标签:

【中文标题】使用 Spring Boot 的 RESTful API 中的循环依赖【英文标题】:Circular Dependencies in RESTful API with Spring Boot 【发布时间】:2017-10-25 10:44:57 【问题描述】:

我正在使用 Spring Boot、JPA 和 Hibernate 以及 Maven 作为存储库管理器开发一个 Rest API。在我的模型中,我有课程Club

@Entity
@Table( name = "CLUB")
public class Club 

   /**
    * Id da entidade
    */
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

    /**
    * Atual técnico do clube.
    */
   @OneToOne(mappedBy = "actualClub")
   private Coach coach;
   
   //Outros atributos, getters e setters
  

上述类与Coach类有@OneToOne关系:

@Entity
@Table(name = "COACH")
public class Coach extends Person 

   /**
    * Clube atual do técnico.
    */
   @OneToOne
   @JoinColumn(name = "CLUB_ID")
   private Club actualClub;

   //Outros atributos, getters e setters
   

Person 类还有一个属性 id,带有 @Id@GeneratedValue 注释。

最后,我还有控制器类ClubController,它处理一些请求,如下所示:

@RestController
public class ClubController 

   /**
    * Instância da classe de serviços da entidade <i>Club</i>
    */
   @Autowired
   private ClubService clubService;

   /**
    * Retorna JSON que representa o clube com o 'id' especificado.
    *
    * @param id Identificador do clube a ser buscado.
    * @return ResponseEntity Objeto com detalhes da requisição HTTP, como o Status.
    */
   @RequestMapping(value = "/clubs/id", produces = MediaType.APPLICATION_JSON_VALUE,
           method = RequestMethod.GET)
   public ResponseEntity<?> getClubById(@PathVariable Long id) 

      final Club club = this.clubService.findById(id);
      if (club != null) 
         return new ResponseEntity<>(club, HttpStatus.FOUND);
       else 
         return new ResponseEntity<>("Não encontrado", HttpStatus.NOT_FOUND);
      
   
   
   /*Entre outros métodos...*/
   

问题是:在/clubs/id 中的GET 中,返回的JSON 内部有一个循环(无限)依赖关系。俱乐部有一个教练,教练也有一个俱乐部,所以它去...... X( @OneToMany relationship betweenClubandPlayer`也会发生这种情况...

我找到了这个解决方案:Link。我只需要在类声明中使用@JsonIdentityInfo 注释。这个解决方案的问题是我不会使用的信息开销(想想@OneToManyPlayer 的关系)。此外,正如您在上面的链接中看到的那样:

“但是,JSON id 引用需要在整个图表中是唯一的,而 JPA id 只需要在同一实体中是唯一的。””

所以,我必须将 UUID 用于 JPA id 字段。

我还找到了另一种可能的解决方案:Spring HATEOAS。在这种情况下,我可以将链接(href)添加到某些属性,然后将申请“按需”。但似乎 Spring HATEOAS 有一些限制,比如支持嵌入资源......

那么,我应该采用哪种方法?还有其他解决方案吗?

很快,我打算在 ios 应用程序中使用这个 API(我不知道它是否有区别)。

对不起,我的英语很差...来自巴西的问候 x)

【问题讨论】:

第二种方法看起来像 Spotify Web API link 。每张专辑都有指向其曲目的 href 链接。 只是为了这个案子,这与我有关:Why I Hate HATEOAS 要解决循环引用,你应该打破两个对象的链接。你可以看到注释JsonManagedReference和JsonBackReference。 【参考方案1】:

这个问题可以通过创建你自己的 DTO (https://en.wikipedia.org/wiki/Data_transfer_object)来解决

数据传输对象 (DTO) 是一种 Java 结构,其创建只是为了组织数据以传输到另一个系统。它是根据需要创建的,没有无关或私人信息,也没有循环引用。

例如:

private Map<String, Object> makeClubDTO(Club club) 
  Map<String, Object> dto = new LinkedHashMap<String, Object>();
  dto.put("id", club.getId());
  dto.put("coach", club.getCoach().getId());
  return dto;

【讨论】:

它对我有用。这听起来是个好主意。仅供参考:Entity To DTO Conversion for a Spring REST API.【参考方案2】:

@JsonIgnoreProperties注释这两个关系

如下

// Employee entity, owning side of relationship 
@ManyToOne(cascade = CascadeType.ALL)
@JsonIgnoreProperties("employee")
private Department department;

// Department side, inverse side of relationship
@OneToMany(mappedBy = "department")
@JsonIgnoreProperties("department")
private Collection<Employee> employee;

【讨论】:

以上是关于使用 Spring Boot 的 RESTful API 中的循环依赖的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Spring Boot App 访问 RESTful 服务

Spring Boot + Spring Security Restful 登录

spring boot:使接口返回统一的RESTful格式数据(spring boot 2.3.1)

spring boot使用TestRestTemplate集成测试 RESTful 接口

Spring Boot 2.x :构建优雅的RESTful接口

Spring Boot构建RESTful API与单元测试