Spring Data REST: Override repository method on the controller

后端 未结 4 807
暖寄归人
暖寄归人 2020-12-05 10:32

I have the following REST repository, whose implementation is generated at runtime by Spring.

@RepositoryRestResource
public interface FooRepository extends          


        
相关标签:
4条回答
  • 2020-12-05 10:45

    Let's imagine we have an Account entity:

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

    With an AccountRepository exposing its CRUD endpoints on /accounts:

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

    And an AccountController that overrides the default GET endpoint form 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);
        }
    

    If you invoke the GET /accounts?size=5&page=0 you will get the following output which is using the mock implementation:

    {
      "_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
      }
    }
    

    Just for the sake of completeness, the POM could be configured with the following parent and dependencies:

    <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>
    
    0 讨论(0)
  • 2020-12-05 10:47

    Just an update that I found that saved my life. As said brilliantly by @mathias-dpunkt in this answer https://stackoverflow.com/a/34518166/2836627

    Most importantly the RepositoryRestController is aware of the spring data rest base path and will be served under this base path.

    So if your base path is "/api" and you are using @RepositoryRestController

    you have to ommit "/api" from @RequestMapping

    0 讨论(0)
  • 2020-12-05 10:57

    Is there a way of overriding REST methods while still keeping the other auto-generated Spring methods?

    Look at the example in the documentation carefully: while not explicitly forbidding class-level requestmapping, it uses method-level requestmapping. I'm not sure if this is the wanted behavior or a bug, but as far as I know this is the only way to make it work, as stated here.

    Just change your controller to:

    @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
        }
    }
    
    0 讨论(0)
  • 2020-12-05 11:03

    I found a neat solution if you are using Java 8 - just use default methods in interface

    @RepositoryRestResource
    public interface FooRepository extends CrudRepository<Foo, Long> {
        default <S extends T> S save(S var1) {
            //do some work here
        }
    }
    
    0 讨论(0)
提交回复
热议问题