Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing custom methods of Spring Data repository and exposing them through REST

I'm trying to add custom methods to my Spring Data repository PersonRepository as described in 1.3 Custom implementations for Spring Data repositories and exposing these method through REST. The initial code is from Accessing JPA Data with REST sample, here is the code for added/modified classes:

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:

{   "_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?

Also, is there a way to dynamically/programmatically add respositories to be exposed via REST?

like image 519
bachr Avatar asked Aug 08 '14 10:08

bachr


People also ask

Is used for exposing spring data repositories over rest using Spring data rest?

Spring Data REST can be used to expose HATEOAS RESTful resources around Spring Data repositories. Without writing a lot of code, we can expose RESTful API around Spring Data Repositories.

How are spring data repositories actually implemented?

In the repository interfaces, we can add the methods like findByCustomerNameAndPhone() (assuming customerName and phone are fields in the domain object). Then, Spring provides the implementation by implementing the above repository interface methods at runtime (during the application run).

What is rest repository in spring boot?

This repository is an interface that lets you perform various operations involving Person objects. It gets these operations by extending the PagingAndSortingRepository interface that is defined in Spring Data Commons. At runtime, Spring Data REST automatically creates an implementation of this interface.


2 Answers

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

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

@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;     }  } 
like image 116
leo Avatar answered Sep 17 '22 04:09

leo


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.

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.

The current solution for this is to craft a custom controller to invoke the repository method.

like image 20
Oliver Drotbohm Avatar answered Sep 17 '22 04:09

Oliver Drotbohm