pageable example data custom java spring rest jpa spring-data

java - example - spring data rest custom controller



Implementando métodos personalizados del repositorio de Spring Data y exponiéndolos a través de REST (5)

Después de dos días, lo he resuelto de esta manera.

Interfaz de repositorio personalizado:

public interface PersonRepositoryCustom { Page<Person> customFind(String param1, String param2, Pageable pageable); }

Implementación de repositorio personalizado

public class PersonRepositoryImpl implements PersonRepositoryCustom{ @Override public Page<Person> customFind(String param1, String param2, Pageable pageable) { // custom query by mongo template, entity manager... } }

Repositorio de datos de primavera:

@RepositoryRestResource(collectionResourceRel = "person", path = "person") public interface PersonRepository extends MongoRepository<Person, String>, PersonRepositoryCustom { Page<Person> findByName(@Param("name") String name, Pageable pageable); }

Representación de recursos de frijoles

public class PersonResource extends org.springframework.hateoas.Resource<Person>{ public PersonResource(Person content, Iterable<Link> links) { super(content, links); } }

Ensamblador de recursos

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

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

Estoy intentando agregar métodos personalizados a mi repositorio de datos de Spring PersonRepository como se describe en 1.3 Implementaciones personalizadas para repositorios de Spring Data y exponer estos métodos a través de REST. El código inicial es de Acceso a datos JPA con muestra REST , aquí está el código para clases añadidas / modificadas:

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

Cuando ejecuto la aplicación y visito http://localhost:8080/portfolio/search/ , obtengo el siguiente cuerpo de respuesta:

{ "_links" : { "findByLastName" : { "href" : "http://localhost:8080/people/search/findByLastName{?name}", "templated" : true } } }

¿Por qué findByFirstName no está expuesto incluso si está disponible en la interfaz de PersonRepository ?

Además, ¿hay alguna forma de agregar repositorios dinámicamente / programáticamente a través de REST?


La razón por la que estos métodos no están expuestos es que básicamente eres libre de implementar lo que quieras en los métodos de repositorio personalizados y, por lo tanto, es imposible razonar sobre el método HTTP correcto para admitir ese recurso en particular.

En su caso, podría estar bien usar un GET simple, en otros casos podría tener que ser un POST ya que la ejecución del método tiene efectos secundarios.

La solución actual para esto es crear un controlador personalizado para invocar el método de repositorio.



Otra opción que utilizamos también es implementar una fábrica de repositorio personalizada para su tipo de almacenamiento específico.

Puede extender desde RepositoryFactoryBeanSupport , crear su propia PersistentEntityInformation y ocuparse de las operaciones de CRUD en una impl de repo predeterminada para su tipo de almacenamiento de datos personalizado. Ver JpaRepositoryFactoryBean por ejemplo. Quizás necesites implementar unas 10 clases en total, pero luego se vuelve reutilizable.


Para los métodos GET , he usado el siguiente enfoque:

  • crear un método @Query ficticio en el Repositorio (LogRepository.java)
  • crear una interfaz personalizada con el mismo método declarado (LogRepositoryCustom.java)
  • crear una implementación de la interfaz personalizada (LogRepositoryImpl.java)

Al usar este enfoque, no tengo que administrar las proyecciones y el ensamblaje de recursos.

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

En la implementación, puede usar los métodos de repositorio o ir directamente a la capa de persistencia:

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