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

bachr picture bachr · Aug 8, 2014 · Viewed 32.8k times · Source

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?

Answer

leo picture leo · Apr 6, 2016

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;
    }

}