将 Spring hateos ResourceAssembler 迁移到 RepresentationModelAssembler

Migrate Spring hateos ResourceAssembler to RepresentationModelAssembler

据此postResourceAssembler改为RepresentationModelAssembler

我有这个使用 Spring HATEOAS 1.0 的代码:

import org.springframework.hateoas.ResourceAssembler;

public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
        implements ResourceAssembler<T, D> {
        ...
}

迁移到 implementation 'org.springframework.boot:spring-boot-starter-hateoas:2.6.4' 之后 我改成了:

public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
        implements RepresentationModelAssembler<T, D> {
        .........
}

但是我得到错误:

Type parameter 'D' is not within its bound; should extend 'org.springframework.hateoas.RepresentationModel<?>'

你知道我该如何解决这个问题吗?

编译器报告类型参数 D 不在您定义的范围内:

public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
        implements RepresentationModelAssembler<T, D> {
        .........
}

换句话说,就是不能用D extends BaseResource实现RepresentationModelAssembler<T, D>(注意这里的类型参数D),因为那个类型应该扩展'org.springframework.hateoas.RepresentationModel<?>'.

RepresentationModelAssembler gives you the ability to convert between domain types, your entities, to RepresentationModels,一个基于 class 的构想,旨在丰富您的 DTO 以收集链接。

定义如下:

public interface RepresentationModelAssembler<T, D extends RepresentationModel<?>>

再次注意类型参数的定义D.

在您的代码中,您需要使用如下内容:

public class BaseAssembler<T extends BaseTransaction, D extends RepresentationModel<?>>
        implements RepresentationModelAssembler<T, D> {
        .........
}

请考虑阅读一些 this or this other 文章,它们提供了各种各样的示例和用例来展示如何实现所需的行为。

例如,给定以下实体,摘自其中一篇被引用的文章:

@Entity
public class Director {
   @Id
   @GeneratedValue
   @Getter
   private Long id;
   @Getter
   private String firstname;
   @Getter
   private String lastname;
   @Getter
   private int year;
   @OneToMany(mappedBy = "director")
   private Set<Movie> movies;
}

以及以下 DTO:

@Builder
@Getter
@EqualsAndHashCode(callSuper = false)
@Relation(itemRelation = "director", collectionRelation = "directors")
public class DirectorRepresentation extends RepresentationModel<DirectorRepresentation> {
   private final String id;
   private final String firstname;
   private final String lastname;
   private final int year;
}

您的 RepresentationModelAssembler 看起来像:

@Component
public class DirectorRepresentationAssembler implements RepresentationModelAssembler<Director, DirectorRepresentation> {
    @Override
    public DirectorRepresentation toModel(Director entity) {
        DirectorRepresentation directorRepresentation = DirectorRepresentation.builder()
                .id(entity.getId())
                .firstname(entity.getFirstname())
                .lastname(entity.getLastname())
                .year(entity.getYear())
                .build();
 
        directorRepresentation.add(linkTo(methodOn(DirectorController.class).getDirectorById(directorRepresentation.getId())).withSelfRel());
        directorRepresentation.add(linkTo(methodOn(DirectorController.class).getDirectorMovies(directorRepresentation.getId())).withRel("directorMovies"));
 
        return directorRepresentation;
    }
 
    @Override
    public CollectionModel<DirectorRepresentation> toCollectionModel(Iterable<? extends Director> entities) {
        CollectionModel<DirectorRepresentation> directorRepresentations = RepresentationModelAssembler.super.toCollectionModel(entities);
 
        directorRepresentations.add(linkTo(methodOn(DirectorController.class).getAllDirectors()).withSelfRel());
 
        return directorRepresentations;
    }
}

就您的接口和对象模型而言:

@Entity
public class Director extends BaseTransaction{
   @Id
   @GeneratedValue
   @Getter
   private Long id;
   @Getter
   private String firstname;
   @Getter
   private String lastname;
   @Getter
   private int year;
   @OneToMany(mappedBy = "director")
   private Set<Movie> movies;
}
public class DirectorRepresentationAssembler
  extends BaseAssembler<Director, DirectorRepresentation>
  implements RepresentationModelAssembler<Director, DirectorRepresentation> {
  //... the above code
}

DirectorRepresentation 同上。

可能有帮助的Spring HATEOAS reference guide itself provides some guidance as well about the changes performed in Spring HATEOAS 1.0 and about how to migrate from the previous version. It even includes a script

无论如何,如上所述,在您的用例中,您只需要修改 BaseAssembler 接口以根据类型 D extends RepresentationModel<?> 进行定义;然后尝试以某种方式将 BaseResource 关联到 RepresentationModel 或摆脱 BaseResource 并改用 RepresentationModel

例如,您可以尝试按如下方式定义 BaseResource

public class BaseResource extends RepresentationModel<BaseResource>{
  // your implementation
}

那么,绑定就对了:

public class BaseAssembler<T extends BaseTransaction, D extends BaseResource>
    implements RepresentationModelAssembler<T, D> {
  // your implementation
}

通过这些更改,DirectorRepresentation 将扩展 BaseResource:

public class DirectorRepresentation extends BaseResource {

}

您可以像这样扩展 BaseAssembler

public class DirectorRepresentationAssembler
    extends BaseAssembler<Director, DirectorRepresentation>
    implements RepresentationModelAssembler<Director, DirectorRepresentation> {
  // your implementation
}

在我看来,您在存储库中发布的代码大部分都很好。我认为唯一的问题是 this line of code,正如我之前提到的,我认为您需要在定义 BaseResource class 时提供类型参数。例如:

package com.hateos.test.entity.web.rest.resource;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import org.joda.time.DateTime;
import org.springframework.hateoas.RepresentationModel;

import java.util.UUID;

public class BaseResource extends RepresentationModel<BaseResource> {

  @JsonProperty
  @ApiModelProperty(position = 1, required = true)
  public UUID id;

  @JsonProperty
  public DateTime creationTime;

  @JsonProperty
  public DateTime lastUpdatedTime;

}

请注意 extends 关键字后包含的代码片段 RepresentationModel<BaseResource>

我不确定它是否会工作,但至少有了这个更改,每个编译都很好,而且似乎工作正常。