web-dev-qa-db-ja.com

Spring Dataリポジトリのカスタムメソッドを実装し、RESTを介して公開する

1.3 Spring Dataリポジトリのカスタム実装 で説明されているように、Spring DataリポジトリPersonRepositoryにカスタムメソッドを追加し、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);  
}

アプリケーションを実行してhttp://localhost:8080/portfolio/search/にアクセスすると、次の応答本文が返されます。

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

findByFirstNameインターフェースで使用可能であってもPersonRepositoryが公開されないのはなぜですか?

また、RESTを介して公開されるリポジトリを動的/プログラムで追加する方法はありますか?

43
bachr

これらのメソッドが公開されない理由は、カスタムリポジトリメソッドで必要なものを基本的に自由に実装できるため、その特定のリソースをサポートする正しいHTTPメソッドについて推論することができないためです。

あなたの場合、プレーンのGETを使用しても問題ないかもしれませんが、他の場合は、メソッドの実行に副作用があるため、POSTである必要があります。

これに対する現在の解決策は、リポジトリメソッドを呼び出すカスタムコントローラーを作成することです。

22
Oliver Drotbohm

2日後、私はこの方法で解決しました。

カスタムリポジトリインターフェース:

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

カスタムリポジトリ実装

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リソース表現

public class PersonResource extends org.springframework.hateoas.Resource<Person>{

    public PersonResource(Person content, Iterable<Link> links) {
        super(content, links);
    }
}

リソースアセンブラー

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

}

カスタム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;
    }

}
24
leo

GETメソッドの場合、次のアプローチを使用しました。

  • ダミー@Queryリポジトリのメソッド(LogRepository.Java)
  • 同じメソッドが宣言されたカスタムインターフェイスを作成します(LogRepositoryCustom.Java)
  • カスタムインターフェイス(LogRepositoryImpl.Java)の実装を作成する

このアプローチを使用すると、投影とリソースの組み立てを管理する必要がありません。

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

実装では、リポジトリメソッドを自由に使用するか、永続層に直接アクセスできます。

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

答えは、指示に従わなかったことです。 PersonRepositoryは両方を拡張する必要がありますPagingAndSortingRepository<Person, Long> AND PersonRepositoryCustominで目的を達成します。 https://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.custom-implementations を参照してください

3
Tommy B

私たちが使用した別のオプションは、特定のストレージタイプにカスタムリポジトリファクトリを実装することです。

RepositoryFactoryBeanSupportから拡張し、独自のPersistentEntityInformationを構築し、カスタムデータストレージタイプのデフォルトリポジトリ実装でCRUD操作を処理できます。たとえば、JpaRepositoryFactoryBeanを参照してください。合計で約10個のクラスを実装する必要があるかもしれませんが、その後再利用可能になります。

1
aux

使用してみてください

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

実装クラス名は、PersonRepositoryCustomImplではなくPersonRepositoryImplにする必要があります。

0