Java 实现 Spring Data 存储库的自定义方法并通过 REST 公开它们
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25201306/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Implementing custom methods of Spring Data repository and exposing them through REST
提问by bachr
I'm trying to add custom methods to my Spring Data repository PersonRepository
as described in 1.3 Custom implementations for Spring Data repositoriesand exposing these method through REST. The initial code is from Accessing JPA Data with RESTsample, here is the code for added/modified classes:
我正在尝试将自定义方法添加到我的 Spring Data 存储库中PersonRepository
,如1.3 Spring Data 存储库的自定义实现中所述,并通过 REST 公开这些方法。初始代码来自使用 REST示例访问 JPA 数据,这是添加/修改类的代码:
interface PersonRepositoryCustom {
List<Person> findByFistName(String name);
}
class PersonRepositoryImpl implements PersonRepositoryCustom, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// initialization here
}
@Override
public List<Person> findByFistName(String name) {
// find the list of persons with the given firstname
}
}
@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(@Param("name") String name);
}
When I run the application and visit http://localhost:8080/portfolio/search/
, I get the following response body:
当我运行应用程序并访问时http://localhost:8080/portfolio/search/
,我得到以下响应正文:
{
"_links" : {
"findByLastName" : {
"href" : "http://localhost:8080/people/search/findByLastName{?name}",
"templated" : true
}
}
}
Why findByFirstName
is not exposed even if it is available in the PersonRepository
interface?
为什么findByFirstName
即使在PersonRepository
接口中可用也不暴露?
Also, is there a way to dynamically/programmatically add respositories to be exposed via REST?
另外,有没有办法动态/以编程方式添加要通过 REST 公开的存储库?
采纳答案by Oliver Drotbohm
The reason these methods are not exposed is that you're basically free to implement whatever you want in custom repository methods and thus it's impossible to reason about the correct HTTP method to support for that particular resource.
这些方法没有公开的原因是您基本上可以自由地在自定义存储库方法中实现您想要的任何内容,因此不可能推断正确的 HTTP 方法来支持该特定资源。
In your case it might be fine to use a plain GET
, in other cases it might have to be a POST
as the execution of the method has side effects.
在您的情况下,使用 plainGET
可能没问题,在其他情况下,它可能必须是 aPOST
因为方法的执行有副作用。
The current solution for this is to craft a custom controller to invoke the repository method.
当前的解决方案是制作一个自定义控制器来调用存储库方法。
回答by aux
Another option we used as well is to implement a custom repository factory for your specific storage type.
我们还使用的另一个选项是为您的特定存储类型实现自定义存储库工厂。
You can extend from RepositoryFactoryBeanSupport
, build your own PersistentEntityInformation
and take care of CRUD ops in a default repo impl for your custom data storage type. See JpaRepositoryFactoryBean
for example. You maybe need to implement about 10 classes in total but then it gets reusable.
您可以在自定义数据存储类型的默认 repo impl 中扩展RepositoryFactoryBeanSupport
、构建自己的PersistentEntityInformation
并处理 CRUD 操作。参见JpaRepositoryFactoryBean
示例。您可能总共需要实现大约 10 个类,然后它就可以重用了。
回答by Tommy B
The answer is that you haven't followed instructions. Your PersonRepository
has to extend both PagingAndSortingRepository<Person, Long>
AND PersonRepositoryCustom
in order to achieve what you're after. See https://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.custom-implementations
答案是您没有按照说明进行操作。您PersonRepository
必须同时扩展PagingAndSortingRepository<Person, Long>
ANDPersonRepositoryCustom
才能实现您所追求的目标。请参阅https://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.custom-implementations
回答by leo
After two days, I have solved in this way.
两天后,我就这样解决了。
Custom Repository Interface:
自定义存储库接口:
public interface PersonRepositoryCustom {
Page<Person> customFind(String param1, String param2, Pageable pageable);
}
Custom Repository Implementation
自定义存储库实现
public class PersonRepositoryImpl implements PersonRepositoryCustom{
@Override
public Page<Person> customFind(String param1, String param2, Pageable pageable) {
// custom query by mongo template, entity manager...
}
}
Spring Data Repository:
弹簧数据存储库:
@RepositoryRestResource(collectionResourceRel = "person", path = "person")
public interface PersonRepository extends MongoRepository<Person, String>, PersonRepositoryCustom {
Page<Person> findByName(@Param("name") String name, Pageable pageable);
}
Bean Resource representation
Bean 资源表示
public class PersonResource extends org.springframework.hateoas.Resource<Person>{
public PersonResource(Person content, Iterable<Link> links) {
super(content, links);
}
}
Resource Assembler
资源组装器
@Component
public class PersonResourceAssembler extends ResourceAssemblerSupport<Person, PersonResource> {
@Autowired
RepositoryEntityLinks repositoryEntityLinks;
public PersonResourceAssembler() {
super(PersonCustomSearchController.class, PersonResource.class);
}
@Override
public PersonResource toResource(Person person) {
Link personLink = repositoryEntityLinks.linkToSingleResource(Person.class, person.getId());
Link selfLink = new Link(personLink.getHref(), Link.REL_SELF);
return new PersonResource(person, Arrays.asList(selfLink, personLink));
}
}
Custom Spring MVC Controller
自定义 Spring MVC 控制器
@BasePathAwareController
@RequestMapping("person/search")
public class PersonCustomSearchController implements ResourceProcessor<RepositorySearchesResource> {
@Autowired
PersonRepository personRepository;
@Autowired
PersonResourceAssembler personResourceAssembler;
@Autowired
private PagedResourcesAssembler<Person> pagedResourcesAssembler;
@RequestMapping(value="customFind", method=RequestMethod.GET)
public ResponseEntity<PagedResources> customFind(@RequestParam String param1, @RequestParam String param2, @PageableDefault Pageable pageable) {
Page personPage = personRepository.customFind(param1, param2, pageable);
PagedResources adminPagedResources = pagedResourcesAssembler.toResource(personPage, personResourceAssembler);
if (personPage.getContent()==null || personPage.getContent().isEmpty()){
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
EmbeddedWrapper wrapper = wrappers.emptyCollectionOf(Person.class);
List<EmbeddedWrapper> embedded = Collections.singletonList(wrapper);
adminPagedResources = new PagedResources(embedded, adminPagedResources.getMetadata(), adminPagedResources.getLinks());
}
return new ResponseEntity<PagedResources>(adminPagedResources, HttpStatus.OK);
}
@Override
public RepositorySearchesResource process(RepositorySearchesResource repositorySearchesResource) {
final String search = repositorySearchesResource.getId().getHref();
final Link customLink = new Link(search + "/customFind{?param1,param2,page,size,sort}").withRel("customFind");
repositorySearchesResource.add(customLink);
return repositorySearchesResource;
}
}
回答by Erik Melleg?rd
For GET
methods I have used the following approach:
对于GET
方法,我使用了以下方法:
- create a dummy
@Query
method in the Repository (LogRepository.java) - create a custom interface with the same method declared (LogRepositoryCustom.java)
- create an implementation of the custom interface (LogRepositoryImpl.java)
@Query
在 Repository (LogRepository.java) 中创建一个虚拟方法- 使用声明的相同方法创建自定义接口 (LogRepositoryCustom.java)
- 创建自定义接口的实现 (LogRepositoryImpl.java)
Using this approach I don't have to manage projections and resource assembling.
使用这种方法,我不必管理预测和资源组装。
@RepositoryRestResource(collectionResourceRel = "log", path = "log")
public interface LogRepository extends PagingAndSortingRepository<Log, Long>,
LogRepositoryCustom {
//NOTE: This query is just a dummy query
@Query("select l from Log l where l.id=-1")
Page<Log> findAllFilter(@Param("options") String options,
@Param("eid") Long[] entityIds,
@Param("class") String cls,
Pageable pageable);
}
public interface LogRepositoryCustom {
Page<Log> findAllFilter(@Param("options") String options,
@Param("eid") Long[] entityIds,
@Param("class") String cls,
Pageable pageable);
}
In the implementation you are free to use the repository methods or going directly to the persistence layer:
在实现中,您可以自由使用存储库方法或直接进入持久层:
public class LogRepositoryImpl implements LogRepositoryCustom{
@Autowired
EntityManager entityManager;
@Autowired
LogRepository logRepository;
@Override
public Page<Log> findAllFilter(
@Param("options") String options,
@Param( "eid") Long[] entityIds,
@Param( "class" ) String cls,
Pageable pageable) {
//Transform kendoui json options to java object
DataSourceRequest dataSourceRequest=null;
try {
dataSourceRequest = new ObjectMapper().readValue(options, DataSourceRequest.class);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
Session s = entityManager.unwrap(Session.class);
Junction junction = null;
if (entityIds != null || cls != null) {
junction = Restrictions.conjunction();
if (entityIds != null && entityIds.length > 0) {
junction.add(Restrictions.in("entityId", entityIds));
}
if (cls != null) {
junction.add(Restrictions.eq("cls", cls));
}
}
return dataSourceRequest.toDataSourceResult(s, Log.class, junction);
}
回答by Binulal Narayanan
Try using
尝试使用
class PersonRepositoryCustomImpl implements PersonRepositoryCustom, InitializingBean {
...
}
回答by Binulal Narayanan
The implementing class name should be PersonRepositoryCustomImpl
instead of PersonRepositoryImpl
.
实现类名应该是PersonRepositoryCustomImpl
而不是PersonRepositoryImpl
.