Java 如何使用Spring-HATEAOS-RepresentationModel/Dto中的独立实体

Java 如何使用Spring-HATEAOS-RepresentationModel/Dto中的独立实体,java,spring,spring-boot,spring-hateoas,Java,Spring,Spring Boot,Spring Hateoas,我使用的是SpringBoot2.2.7、SpringHateAOS1.0、SpringJPA、SpringDataREST和Hibernate。我用一个复杂的模型创建了一个服务器REST 到目前为止,我部署了API,在数据REST允许的情况下向世界公开实体。所以我的DTO等于我的实体(即使我有几个Spring投影)。 然而,经过多次阅读,我发现我所做的并不是最佳实践,我想将实体与Dto分开,以保留HATEOAS 我查找了一些复杂的示例,但没有发现任何对实际应用程序真正有用的东西。我知道HATE

我使用的是SpringBoot2.2.7、SpringHateAOS1.0、SpringJPA、SpringDataREST和Hibernate。我用一个复杂的模型创建了一个服务器REST

到目前为止,我部署了API,在数据REST允许的情况下向世界公开实体。所以我的DTO等于我的实体(即使我有几个Spring投影)。 然而,经过多次阅读,我发现我所做的并不是最佳实践,我想将
实体
Dto
分开,以保留HATEOAS

我查找了一些复杂的示例,但没有发现任何对实际应用程序真正有用的东西。我知道HATEOAS的使用并不多,但我在客户机上做了很多努力来利用它

我正在寻找一种在我的数据库上执行查询、获取数据并将其转换为Dto的简洁方法

要将数据从
Entity
复制到
Dto
,我使用的是Mapstructs:我希望尽可能减少样板代码

假设我有这个实体:

@Entity
@Data
@Builder
public class EntityA implements Persistable<Long>, Serializable{
     private long id;
     private String 

     //other fields
}
@实体
@资料
@建筑商
公共类EntityA实现可持久化、可序列化{
私人长id;
私有字符串
//其他领域
}
这个实体呢

@实体

@Data
@Builder
public class EntityB implements Persistable<Long>, Serializable{
    @ToString.Exclude    
    @NotNull
    @OnDelete(action = OnDeleteAction.CASCADE)
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Document document;

    //other fields    
}
@数据
@建筑商
公共类EntityB实现可持久化、可序列化{
@ToString.Exclude
@NotNull
@OnDelete(action=OnDeleteAction.CASCADE)
@manytone(fetch=FetchType.LAZY,可选=false)
私人文件;
//其他领域
}
我将实体存储库创建为:

@Transactional
@PreAuthorize("isAuthenticated()")
public interface ContactRepository extends JpaRepository<EntityA, Long>, ContactCustomRepository<EntityA, Long>, JpaSpecificationExecutor<EntityA> {

}
@Transactional
@预授权(“isAuthenticated()”)
公共接口ContactRepository扩展了JpaRepository、ContactCustomRepository、JpaSpecificationExecutor{
}
我的控制器是:

@RepositoryRestController
@PreAuthorize("isAuthenticated()")
@Log4j2
public class EntityAController {

 @GetMapping(path = "/entityA/{id:[0-9]+}")
    public ResponseEntity<?> get(@PathVariable(value = "id") long id) {
        return ResponseEntity.ok(/*assembler??*/.toModel(myservice.get(id)));
    }
@RepositoryRestController
@预授权(“isAuthenticated()”)
@Log4j2
公共类实体控制器{
@GetMapping(path=“/entityA/{id:[0-9]+}”)
公共响应属性获取(@PathVariable(value=“id”)长id){
返回ResponseEntity.ok(/*汇编程序???*/.toModel(myservice.get(id));
}
我还缺少一些难题:如何定义Dto以及如何创建一个不需要复制Dto中已经定义的代码的汇编器

我试图将Dto创建为:

@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonRootName(value = "entityA")
@Relation(collectionRelation = "entitiesA")
@Builder
public class EntityAModel extends RepresentationModel<EntityA> implements Serializable {
     private long id;
     private String 
     //other fields
}
@数据
@AllArgsConstructor
@EqualsAndHashCode(callSuper=false)
@JsonRootName(value=“entityA”)
@关系(collectionRelation=“entitiesA”)
@建筑商
公共类EntityAModel扩展了RepresentationModel,实现了可序列化{
私人长id;
私有字符串
//其他领域
}
关于实体B,我更不确定:

@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@JsonRootName(value = "entityB")
@Relation(collectionRelation = "entitiesB")
@Builder
public class EntityBModel extends RepresentationModel<EntityB> implements Serializable {
     private EntityA entityA;
     //other fields
}
@数据
@AllArgsConstructor
@EqualsAndHashCode(callSuper=false)
@JsonRootName(value=“entityB”)
@关系(collectionRelation=“entitiesB”)
@建筑商
公共类EntityBModel扩展了RepresentationModel实现了可序列化{
私人实体a实体a;
//其他领域
}
我创建了一个通用汇编程序:

/**
 * A {@link SimpleRepresentationModelAssembler} that mixes together a Spring web controller and a
 * {@link LinkRelationProvider} to build links upon a certain strategy.
 *
 * @author Greg Turnquist
 */
public class SimpleIdentifiableRepresentationModelAssembler<T> implements SimpleRepresentationModelAssembler<T> {

    /**
     * The Spring MVC class for the object from which links will be built.
     */
    private final Class<?> controllerClass;

    /**
     * A {@link LinkRelationProvider} to look up names of links as options for resource paths.
     */
    @Getter
    private final LinkRelationProvider relProvider;

    /**
     * A {@link Class} depicting the object's type.
     */
    @Getter
    private final Class<?> resourceType;

    /**
     * Default base path as empty.
     */
    @Getter
    @Setter
    private String basePath = "";

    /**
     * Default a assembler based on Spring MVC controller, resource type, and {@link LinkRelationProvider}. With this
     * combination of information, resources can be defined.
     *
     * @param controllerClass - Spring MVC controller to base links off of
     * @param relProvider
     * @see #setBasePath(String) to adjust base path to something like "/api"/
     */
    public SimpleIdentifiableRepresentationModelAssembler(Class<?> controllerClass, LinkRelationProvider relProvider) {

        this.controllerClass = controllerClass;
        this.relProvider = relProvider;

        // Find the "T" type contained in "T extends Identifiable<?>", e.g.
        // SimpleIdentifiableRepresentationModelAssembler<User> -> User
        this.resourceType = GenericTypeResolver.resolveTypeArgument(this.getClass(),
                SimpleIdentifiableRepresentationModelAssembler.class);
    }

    /**
     * Alternate constructor that falls back to {@link EvoInflectorLinkRelationProvider}.
     *
     * @param controllerClass
     */
    public SimpleIdentifiableRepresentationModelAssembler(Class<?> controllerClass) {
        this(controllerClass, new EvoInflectorLinkRelationProvider());
    }

    /**
     * Add single item self link based on the object and link back to aggregate root of the {@literal T} domain type using
     * {@link LinkRelationProvider#getCollectionResourceRelFor(Class)}}.
     *
     * @param resource
     */
    @Override
    public void addLinks(EntityModel<T> resource) {

        resource.add(getCollectionLinkBuilder().slash(getId(resource)).withSelfRel());
        resource.add(getCollectionLinkBuilder().withRel(this.relProvider.getCollectionResourceRelFor(this.resourceType)));
    }

    private Object getId(EntityModel<T> resource) {

        Field id = ReflectionUtils.findField(this.resourceType, "id");
        ReflectionUtils.makeAccessible(id);

        return ReflectionUtils.getField(id, resource.getContent());
    }

    /**
     * Add a self link to the aggregate root.
     *
     * @param resources
     */
    @Override
    public void addLinks(CollectionModel<EntityModel<T>> resources) {
        resources.add(getCollectionLinkBuilder().withSelfRel());
    }

    /**
     * Build up a URI for the collection using the Spring web controller followed by the resource type transformed by the
     * {@link LinkRelationProvider}. Assumption is that an {@literal EmployeeController} serving up {@literal Employee}
     * objects will be serving resources at {@code /employees} and {@code /employees/1}. If this is not the case, simply
     * override this method in your concrete instance, or resort to overriding {@link #addLinks(EntityModel)} and
     * {@link #addLinks(CollectionModel)} where you have full control over exactly what links are put in the individual
     * and collection resources.
     *
     * @return
     */
    protected LinkBuilder getCollectionLinkBuilder() {

        WebMvcLinkBuilder linkBuilder = linkTo(this.controllerClass);

        for (String pathComponent : (getPrefix() + this.relProvider.getCollectionResourceRelFor(this.resourceType))
                .split("/")) {
            if (!pathComponent.isEmpty()) {
                linkBuilder = linkBuilder.slash(pathComponent);
            }
        }

        return linkBuilder;
    }

    /**
     * Provide opportunity to override the base path for the URI.
     */
    private String getPrefix() {
        return getBasePath().isEmpty() ? "" : getBasePath() + "/";
    }
}
/**
*混合了SpringWeb控制器和
*{@LinkRelationProvider}在特定策略上建立链接。
*
*@作者格雷格·特恩奎斯特
*/
公共类SimpleIdentifiableRepresentationModelAssembler实现SimpleRepresentationModelAssembler{
/**
*将从中构建链接的对象的Spring MVC类。
*/
私有最终类控制器类;
/**
*一个{@LinkRelationProvider},用于查找作为资源路径选项的链接名称。
*/
@吸气剂
私人最终链接RelationProvider relProvider;
/**
*描述对象类型的{@link Class}。
*/
@吸气剂
私有最终类资源类型;
/**
*默认基本路径为空。
*/
@吸气剂
@塞特
私有字符串basePath=“”;
/**
*基于SpringMVC控制器、资源类型和{@link-LinkRelationProvider}的默认汇编程序
*结合信息,可以定义资源。
*
*@param controllerClass-从Spring MVC控制器到基本链路
*@param relProvider
*@see#setBasePath(String)将基本路径调整为“/api”之类的内容/
*/
公共SimpleIdentifiableRepresentationModelAssembler(类controllerClass,LinkRelationProvider relProvider){
this.controllerClass=controllerClass;
this.relProvider=relProvider;
//查找“T”中包含的“T”类型,例如。
//SimpleIdentifiableRepresentationModelAssembler->User
this.resourceType=GenericTypeResolver.resolveTypeArgument(this.getClass(),
SimpleIdentifiableRepresentationModelAssembler.class);
}
/**
*返回到{@link-evoinfactorlinkrelationprovider}的备用构造函数。
*
*@param控制器类
*/
公共SimpleIdentifiableRepresentationModelAssembler(类控制器类){
这(controllerClass,新的EvoIncomectorLinkRelationProvider());
}
/**
*基于对象添加单项自链接,并使用
*{@LinkRelationProvider#getCollectionResourceRelFor(类)}。
*
*@param资源
*/
@凌驾
公共void addLinks(EntityModel资源){
add(getCollectionLinkBuilder().slash(getId(resource)).withSelfRel());
add(getCollectionLinkBuilder().withRel(this.relProvider.getCollectionResourceRelFor(this.resourceType)));
}
私有对象getId(EntityModel资源){
字段id=ReflectionUtils.findField(this.resourceType,“id”);
ReflectionUtils.makeAccessible(id);
返回ReflectionUtils.getField(id,resource.getContent());
}
/**
*向聚合根添加自链接。
*
*@param资源
*/
@凌驾
公共void addLinks(CollectionModel资源){
resources.add(getCollectionLi
@Component
public
class EntityAModelAssembler extends SimpleIdentifiableRepresentationModelAssembler<EntityAModel> {

    DocumentFullModelAssembler() {
        super(EntityAController.class);
    }
}