it-swarm.com.de

Benutzerdefinierte Methoden des Spring Data-Repository implementieren und durchsetzen REST

Ich versuche, meinem Spring Data-Repository PersonRepository benutzerdefinierte Methoden hinzuzufügen, wie in 1.3 Benutzerdefinierte Implementierungen für Spring Data-Repositorys beschrieben und diese Methode durch REST verfügbar machen. Der ursprüngliche Code stammt von Zugriff auf JPA-Daten mit REST sample, hier ist der Code für hinzugefügte/modifizierte Klassen:

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

Wenn ich die Anwendung starte und http://localhost:8080/portfolio/search/ besuche, erhalte ich den folgenden Antworttext:

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

Warum ist findByFirstName nicht verfügbar, auch wenn sie in der PersonRepository-Schnittstelle verfügbar ist?

Gibt es auch eine Möglichkeit, dynamisch/programmgesteuert über REST freigelegte Ressorts hinzuzufügen?

42
bachr

Der Grund dafür, dass diese Methoden nicht verfügbar gemacht werden, besteht darin, dass Sie grundsätzlich beliebige Elemente in benutzerdefinierten Repository-Methoden implementieren können. Daher ist es unmöglich, über die richtige HTTP-Methode zu entscheiden, die für diese bestimmte Ressource unterstützt wird.

In Ihrem Fall ist es in Ordnung, ein einfaches GET zu verwenden. In anderen Fällen muss es sich um ein POST handeln, da die Ausführung der Methode Nebenwirkungen hat.

Die aktuelle Lösung hierfür besteht darin, einen benutzerdefinierten Controller zu erstellen, um die Repository-Methode aufzurufen.

21
Oliver Drotbohm

Nach zwei Tagen habe ich auf diese Weise gelöst.

Benutzerdefinierte Repository-Schnittstelle:

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

Benutzerdefinierte Repository-Implementierung

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 Darstellung

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

}
22
leo

Für GET-Methoden habe ich den folgenden Ansatz verwendet:

  • erstellen Sie eine Dummy-@Query-Methode im Repository (LogRepository.Java).
  • erstellen Sie eine benutzerdefinierte Schnittstelle mit derselben deklarierten Methode (LogRepositoryCustom.Java).
  • erstellen Sie eine Implementierung der benutzerdefinierten Schnittstelle (LogRepositoryImpl.Java).

Bei diesem Ansatz muss ich keine Projektionen verwalten und Ressourcen zusammenstellen.

@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 der Implementierung können Sie die Repository-Methoden verwenden oder direkt zur Persistenzschicht wechseln:

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);
}
8
Erik Mellegård

Die Antwort ist, dass Sie die Anweisungen nicht befolgt haben. Ihre PersonRepository muss sowohl PagingAndSortingRepository<Person, Long> als auch PersonRepositoryCustom erweitern, um das zu erreichen, wonach Sie suchen. Siehe https://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.custom-implementations

2
Tommy B

Eine weitere Option, die wir verwendet haben, ist die Implementierung einer benutzerdefinierten Repository-Factory für Ihren spezifischen Speichertyp.

Sie können eine Erweiterung von RepositoryFactoryBeanSupport durchführen, Ihre eigene PersistentEntityInformation erstellen und CRUD-Operationen in einem Standard-Repo-Impl für Ihren benutzerdefinierten Datenspeichertyp erledigen. Siehe beispielsweise JpaRepositoryFactoryBean. Möglicherweise müssen Sie insgesamt etwa 10 Klassen implementieren, die dann wiederverwendbar werden.

1
aux

Versuchen Sie es mit 

class PersonRepositoryCustomImpl implements PersonRepositoryCustom, InitializingBean {
    ...
}
0

Der implementierende Klassenname sollte PersonRepositoryCustomImpl anstelle von PersonRepositoryImpl sein.

0