web-dev-qa-db-ja.com

Spring Data REST:コントローラーのリポジトリメソッドをオーバーライドする

次のREST=リポジトリがあり、その実装はSpringによって実行時に生成されます。

@RepositoryRestResource
public interface FooRepository extends CrudRepository<Foo, Long> {

}

これは、save()、find()、exists()、およびRESTを介して使用可能および公開される他のメソッドを用意することを意味します。

ここで、メソッドの1つをオーバーライドします。たとえば、save()。そのために、次のようにそのメソッドを公開するコントローラーを作成します。

@RepositoryRestController
@RequestMapping("/foo")
public class FooController {

    @Autowired
    FooService fooService;


    @RequestMapping(value = "/{fooId}", method = RequestMethod.PUT)
    public void updateFoo(@PathVariable Long fooId) {
        fooService.updateProperly(fooId);
    }

}

問題:このコントローラーを有効にすると、Springによって実装された他のメソッドはすべて公開されなくなります。したがって、たとえば、/ foo/1に対してGETリクエストを実行できなくなりました

質問:他の自動生成されたSpringメソッドを維持しながら、RESTメソッドをオーバーライドする方法はありますか?

追加情報:

  1. この質問は非常に似ているようです: Spring Data Rest:同じrequest-mapping-pathを使用したRestControllerのメソッドのオーバーライド ...

  2. @RepositoryEventHandlerを使用することを考えましたが、サービスの下にカプセル化したいので、その考えはあまり好きではありません。また、トランザクションコンテキストの制御を失うようです。

  3. Spring Dataドキュメントのこの部分 は次のように述べています:

    特定のリソース用のカスタムハンドラを作成する場合があります。 Spring Data RESTの設定、メッセージコンバーター、例外処理などを活用するには、標準のSpring MVC @Controllerまたは@RestControllerの代わりに@RepositoryRestControllerアノテーションを使用します

箱から出してすぐに動作するはずですが、残念ながら動作しません。

16
Nicolas

他の自動生成されたSpringメソッドを保持したまま、REST=メソッドをオーバーライドする方法はありますか?

ドキュメントの例を注意深く見てください。クラスレベルのリクエストマッピングを明示的に禁止するわけではありませんが、メソッドレベルのリクエストマッピングを使用します。これが望ましい動作なのかバグなのかはわかりませんが、私が知る限り、これが here のように機能する唯一の方法です。

コントローラーを次のように変更するだけです。

@RepositoryRestController
public class FooController {

    @Autowired
    FooService fooService;

    @RequestMapping(value = "/foo/{fooId}", method = RequestMethod.PUT)
    public void updateFoo(@PathVariable Long fooId) {
        fooService.updateProperly(fooId);
    }

    // edited after Sergey's comment
    @RequestMapping(value = "/foo/{fooId}", method = RequestMethod.PUT)
    public RequestEntity<Void> updateFoo(@PathVariable Long fooId) {
        fooService.updateProperly(fooId);

        return ResponseEntity.ok().build(); // simplest use of a ResponseEntity
    }
}
12
Marc Tarin

Accountエンティティがあるとします:

@Entity
public class Account implements Identifiable<Integer>, Serializable {

    private static final long serialVersionUID = -3187480027431265380L;

    @Id
    private Integer id;
    private String name;

    public Account(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

AccountRepositoryを使用して、/accountsのCRUDエンドポイントを公開します。

@RepositoryRestResource(collectionResourceRel = "accounts", path = "accounts")
public interface AccountRepository extends CrudRepository<Account, Integer> {
} 

デフォルトのAccountControllerエンドポイント形式GETをオーバーライドするAccountRepository

@RepositoryRestController
public class AccountController {
    private PagedResourcesAssembler<Account> pagedAssembler;

    @Autowired
    public AccountController(PagedResourcesAssembler<Account> pagedAssembler) {
        this.pagedAssembler = pagedAssembler;
    }

    private Page<Account> getAccounts(Pageable pageRequest){
        int totalAccounts= 50;
        List<Account> accountList = IntStream.rangeClosed(1, totalAccounts)
                                             .boxed()
                                             .map( value -> new Account(value, value.toString()))
                                             .skip(pageRequest.getOffset())
                                             .limit(pageRequest.getPageSize())
                                             .collect(Collectors.toList());
        return new PageImpl(accountList, pageRequest, totalAccounts);
    }

    @RequestMapping(method= RequestMethod.GET, path="/accounts", produces = "application/hal+json")
    public ResponseEntity<Page<Account>> getAccountsHal(Pageable pageRequest, PersistentEntityResourceAssembler assembler){
        return new ResponseEntity(pagedAssembler.toResource(getAccounts(pageRequest), (ResourceAssembler) assembler), HttpStatus.OK);
    }

GET /accounts?size=5&page=0を呼び出すと、モック実装を使用している次の出力が得られます。

{
  "_embedded": {
    "accounts": [
      {
        "name": "1",
        "_links": {
          "self": {
            "href": "http://localhost:8080/accounts/1"
          },
          "account": {
            "href": "http://localhost:8080/accounts/1"
          }
        }
      },
      {
        "name": "2",
        "_links": {
          "self": {
            "href": "http://localhost:8080/accounts/2"
          },
          "account": {
            "href": "http://localhost:8080/accounts/2"
          }
        }
      },
      {
        "name": "3",
        "_links": {
          "self": {
            "href": "http://localhost:8080/accounts/3"
          },
          "account": {
            "href": "http://localhost:8080/accounts/3"
          }
        }
      },
      {
        "name": "4",
        "_links": {
          "self": {
            "href": "http://localhost:8080/accounts/4"
          },
          "account": {
            "href": "http://localhost:8080/accounts/4"
          }
        }
      },
      {
        "name": "5",
        "_links": {
          "self": {
            "href": "http://localhost:8080/accounts/5"
          },
          "account": {
            "href": "http://localhost:8080/accounts/5"
          }
        }
      }
    ]
  },
  "_links": {
    "first": {
      "href": "http://localhost:8080/accounts?page=0&size=5"
    },
    "self": {
      "href": "http://localhost:8080/accounts?page=0&size=5"
    },
    "next": {
      "href": "http://localhost:8080/accounts?page=1&size=5"
    },
    "last": {
      "href": "http://localhost:8080/accounts?page=9&size=5"
    }
  },
  "page": {
    "size": 5,
    "totalElements": 50,
    "totalPages": 10,
    "number": 0
  }
}

完全を期すために、POMは次の親と依存関係で構成できます。

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-rest-webmvc</artifactId>
            <version>2.6.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
    </dependencies>
8
Daniel Cerecedo

Java 8-インターフェイスでデフォルトのメソッドを使用するだけの場合、きちんとした解決策を見つけました

@RepositoryRestResource
public interface FooRepository extends CrudRepository<Foo, Long> {
    default <S extends T> S save(S var1) {
        //do some work here
    }
}
2
jsannn

更新を見つけただけで、命が救われました。この答えで@ mathias-dpunktが見事に言ったように https://stackoverflow.com/a/34518166/2836627

最も重要なことは、RepositoryRestControllerはスプリングデータレストのベースパスを認識しており、このベースパスの下で提供されることです。

したがって、ベースパスが「/ api」で、@ RepositoryRestControllerを使用している場合

@RequestMappingから「/ api」を省略する必要があります

1
azous