【问题标题】:Spring - How to apply a projection on a single resource?Spring - 如何在单个资源上应用投影?
【发布时间】:2017-09-19 05:43:14
【问题描述】:

我正在尝试在名为 Institute 的实体类上应用投影。

我定义了以下投影类。

@Projection(name = "instituteProjection", types = { Institute.class })
public interface InstituteProjection {

    String getOrganizationName();

    Contact getContact();

    Address getRegisteredAddress();

    Address getMailingAddress();

}

我遵循了 Oliver Gierke link 的回答,并且能够在调用 http://localhost:8080/institutes 时返回带有投影的集合资源。我通过在服务层实现以下方法,然后使用 REST 控制器调用它来做到这一点。

@Autowired
private ProjectionFactory projectionFactory;

@Autowired
InstituteTypeRepository instituteTypeRepo;

@Override
public PagedResources<Institute> getAllInstitutes(Pageable page) {
    Page<?> instituteList = instituteRepo.findAll(page).
            map(institute -> projectionFactory.createProjection(InstituteListProjection.class, institute));
    PagedResources<Institute> instituteListPaged = pagedResourcesAssembler.toResource(instituteList);
    return instituteListPaged;

}

现在,当调用 http://localhost:8080/institutes/1 时,如何将相同的投影应用于项目资源?

更新 1:

获取单个资源的控制器方法

@RequestMapping(value = "institutes/{instituteId}", method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getInstitute(@PathVariable Long instituteId) {
    Institute institute = service.getInstitute(instituteId);
    return new ResponseEntity<>(institute, HttpStatus.OK);
}

更新 2:

从存储库中获取单个资源的服务层方法

@Override
public Institute getInstitute(Long instituteId) {
    Institute institute = instituteRepo.findOne(instituteId);
    return institute;
}

【问题讨论】:

  • 你看我的回答了吗?有帮助吗?
  • 你能用控制器逻辑更新问题吗

标签: java spring spring-mvc spring-data-rest


【解决方案1】:

在最新的 Spring Data Rest 版本中可以轻松完成!

您需要做的就是:

  1. 将投影名称作为请求参数传递

    `/institutes/1?projection=instituteProjection`
    
  2. 从您的服务方法中返回 PersistentEntityResource 而不是 Institute

完成!

我假设你想从你的自定义控制器中调用这个服务方法,在这种情况下你需要从你的控制器方法中返回ResponseEntity&lt;PersistentEntityResource&gt;

Spring Data Rest 负责将 @Projections 应用到 PersistentEntityResources 的 api 请求上,就像你不断从 @RepositoryRestResource 暴露你的 @RestResource;投影的相同行为,保持相同的命名约定,基本相同的 URI(对于当前示例)。

带有一点业务逻辑的服务方法可能如下所示:

    @Override
    @Transactional(readOnly = true)
    public PersistentEntityResource getInstitute(Long instituteId, PersistentEntityResourceAssembler resourceAssembler) {
        Institute institute = instituteRepo.findOne(instituteId);
        return resourceAssembler.toModel(institute);
    }

您的控制器方法可能如下所示:

    @GetMapping(value = "institutes/{instituteId}")
    ResponseEntity<PersistentEntityResource> getInstitute(@PathVariable Long instituteId,
                                                                    PersistentEntityResourceAssembler resourceAssembler) {
        return ResponseEntity.status(HttpStatus.OK)
                .body(service.getInstitute(instituteId, resourceAssembler));
    }
}

spring-data-rest 3.3.4.RELEASE

也可以查看更简单的方法to return a collection resource with the projections

【讨论】:

    【解决方案2】:

    您的问题有些不清楚。您可以自动将 Spring Data 存储库公开为剩余资源,即您不需要定义自己的 Spring MVC 控制器。

    http://docs.spring.io/spring-data/rest/docs/current/reference/html/#install-chapter

    使用 SDR 存储库,定义为“摘录投影”的投影可以自动应用于集合资源:

    http://docs.spring.io/spring-data/rest/docs/current/reference/html/#projections-excerpts.excerpts

    但不是单一资源。对于单一资源,客户端必须通过请求参数指定所需的投影。

    如果您想将投影自动应用于单个资源,那么这可能是实现您自己的控制器的一个原因(但是您的问题不清楚这是否是您正在做的事情)。

    有关自动将投影应用于单个资源的替代方法(即没有自定义控制器,请参见此处):

    Spring Data REST: projection representation of single resource

    如果您正在创建自定义控制器以自动应用投影,则可以简单地编写自定义查询方法,返回 Projection 而不是完整实体并从您的控制器/服务调用它:

    https://spring.io/blog/2016/05/03/what-s-new-in-spring-data-hopper#projections-on-repository-query-methods

    基本上,通过一些配置,您可以选择自动公开为 REST 资源的 Spring Data 存储库,而无需编写任何控制器代码。

    【讨论】:

    • 如何在不使用自定义 REST 控制器的情况下调用服务层?我知道拥有 SDR 生成的控制器会为我节省大量代码。
    • 如果您想进行额外处理以响应创建/更新/删除 REST 请求,那么您可以使用事件处理程序。 docs.spring.io/spring-data/rest/docs/current/reference/html/…
    • 我之前尝试过使用 SDR 生成的控制器来执行此操作,但这使得使用实体类上的注释进行的约束验证会引发 JPA 回滚异常,而不是在未满足约束时给出 ConstraintViolationException。所以我不得不在 RepositoryEventHandler 类中编写手动验证,这很不方便。
    • 您可以使用 JSR303 注释或 Spring Validator 实现在 SDR 中进行验证,就像在 Spring MVC 控制器中一样。我手头没有所需的配置,但我确信周围有很多资源。
    • 使用自定义控制器,我只需将@Valid 注释添加到 POST 方法并验证实体,这将引发特定的 ConstraintViolationException。但是,如果我使用 SDR 生成的控制器,它会引发 JPA 回滚异常。我不知道被违反的具体约束。有没有办法解决这个问题
    【解决方案3】:

    在你的控制器或@RestController 中你应该声明一个@PathVariable。也许该线程将为您服务:Spring mvc @PathVariable 因此,您使用路径变量解析来实现控制器,然后使用 PathVariable 作为参数调用您的服务

    【讨论】:

    • 我已经实现了控制器方法来获取单个资源。我将在问题中包含该部分,以便您查看。现在我正在调用服务层以通过存储库获取资源。那么当我从存储库中获取资源时如何应用投影呢?
    猜你喜欢
    • 2017-09-27
    • 2017-03-10
    • 2015-04-06
    • 2018-03-29
    • 1970-01-01
    • 2014-10-03
    • 1970-01-01
    • 2015-06-05
    • 2018-08-06
    相关资源
    最近更新 更多