在我目前正在使用的Spring Boot项目中,我们使用了一个自定义的Spring Data JPA基本存储库,我们在其中添加了一个find()
方法,该方法使用了我们的自定义查询功能。根据{{3}},我们有CustomRepository
扩展CrudRepository
,我们有CustomRepositoryImpl
扩展SimpleJpaRepository
,我们@EnableJpaRepositories(repositoryBaseClass = CustomRepositoryImpl.class)
@Configuration
{1}}上课。
到目前为止,这么好。在数据方面,一切都很完美。但是,我们在使用Spring Data REST将自定义功能暴露给REST API时遇到了一些问题。有两个主要问题:1)实际上将自定义find()
方法公开为REST端点,以及2)将此端点添加到特定存储库级别上的HAL链接。我将详细介绍这两个问题。
这里的问题是我们显然希望以通用的方式做到这一点。现在,如果我们有一个Foo
实体,我们已经创建了一个FooRepository extends CustomRepository<Foo>
接口,我们需要创建一个RestController:
@RepositoryRestController
@RequestMapping("/api")
public class FooRestController {
@Autowired
private FooRepository fooRepository;
@Autowired
private PagedResourcesAssembler pagedResourcesAssembler;
@RequestMapping(value = "/foo/find", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public PagedResources<PersistentEntityResource> findEndPoint(
@RequestParam Map<String, String> params,
Pageable pageable,
PersistentEntityResourceAssembler resourceAssembler) throws Exception {
Page<Foo> page = fooRepository.find(params, pageable);
return pagedResourcesAssembler.toResource(page, resourcesAssembler);
}
}
这可以按预期工作。但是,假设我们现在有另一个名为Bar
的实体,我们必须创建与上面完全相同的方法,将每个Foo
替换为Bar
,将每个foo
替换为{{1} }}。出于我们项目的目的,这是违反the Spring Documentation的不可接受的行为。
总结第一个问题:我们所有的存储库都会扩展bar
,它有一个通用类型CustomRepository
方法,其中T是存储库&#39; s实体类型参数。对于扩展我们Page<T> find()
的每个存储库,我们需要一个REST端点CustomRepository
。如何在不重复每个存储库的情况下实现这一目标。
我目前还没有找到使用Spring Data REST在存储库级别添加HAL链接的正确方法。目前,我了解/api/<the Entity for that repository>/find
的三个选项,即ResourceProcessor
,Resource<Entity>
和RepositoryLinksResource
。
RepositorySearchesResource
将ResourceProcessor<Resource<Entity>>
调用中返回的链接添加到每个序列化实体。鉴于我们正在使用存储库级process
方法,这显然不是我们想要的。
find()
似乎仅将链接添加到api的根级别,但我承认我并不完全确定ResourceProcessor<RepositoryLinksResource>
能做什么或不能做什么。如果我是对的,那也不是我们想要的。在根级别,我们希望找到指向RepositoryLinksResource
和/api/foo
的链接,并且只有在/api/bar
级别才会看到/api/foo
链接。
/api/foo/find
似乎最接近,因为它确实添加了到存储库级别的链接,这正是我们正在寻找的。但是,它与Spring机制紧密结合,后者为存储库中定义的ResourceProcessor<RepositorySearchesResource>
方法生成实现,这意味着如果您的任何存储库中没有findByProperty
方法,{{永远不会调用方法。
现在,我们通过黑客攻击最后一个选项来解决这个问题。我们有findByProperty
,其中使用已实施的process
方法添加了我们的自定义FooRestController implements ResourceProcessor<RepositorySearchesResource>
方法。我们通过向process(...)
界面添加/api/foo/find
方法来强制调用process
(我们可以这样做,因为我们的findById(Long id)
具有CustomRepository
类型参数,并且我们所有的CustomRepository
都有T extends BaseEntity
)。然而,这是一个非常丑陋的黑客攻击,因为它在BaseEntity
中添加了冗余方法和冗余REST端点,它提供了与Long id
完全相同的结果。它还要求我们为每个实体做同样的事情,再次严重违反DRY。
总结第二个问题:,鉴于我们在/api/foo/search/findById
中有自定义/api/foo/{id}
方法,我们希望在HAL中添加find()
链接扩展我们CustomRepository
的每个存储库的链接。此链接应该在存储库级别(/api/entity/find
)上可见,我们不需要重复。
答案 0 :(得分:0)
public interface EntityRepository extends CrudRepository<Entity, Integer> {
@Query("SELECT e FROM Entity e WHERE e.custom = 1 ")
List<Entity> find();
}
在存储库上使用@RepositoryRestController
可为给定的存储库创建端点。
您也可以手动执行此操作,方法是将requestmapping中的foo替换为变量,并使用该变量来确定存储库的类型。
@RequestMapping(method = RequestMethod.GET, value = "/{repository}/find")
public Entity find(@PathVariable String repository) {
Entity entity;
switch(repository){
case "Foo": entity = fooRepository.find();
break;
case "Bar": entity = barRepository.find();
}
spring-hateoas
项目为此提供了很好的静态导入,使用泛型这应该有帮助。例如:import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
resource.add(linkTo(methodOn(EntityController.class).getParent(entity.getParentId())).withRel("entityParent"));
答案 1 :(得分:0)
你应该删除产品属性。并省略@RequestMapping(“/ api”)Anonation
因为当定义了@RepositoryRestController注释
时,spring数据会默认为控制器添加前缀我编辑一些代码,就像这样:
@RepositoryRestController
public class FooRestController {
@Autowired
private FooRepository fooRepository;
@Autowired
private PagedResourcesAssembler pagedResourcesAssembler;
@RequestMapping(value = "/foo/find", method = RequestMethod.GET)
@ResponseBody
public PagedResources<PersistentEntityResource> findEndPoint(
@RequestParam Map<String, String> params,
Pageable pageable,
PersistentEntityResourceAssembler resourceAssembler) throws Exception {
Page<Foo> page = fooRepository.find(params, pageable);
return pagedResourcesAssembler.toResource(page, resourcesAssembler);
}
}
我使用的是spring boot 2.0版本。