Search code examples
springspring-dataentityspring-data-jpahateoas

Spring RepositoryRestController with excerptProjection


I have defined a @Projection for my Spring Data entity as described here

For the same reasons as described there. When I do GET request, everything is returned as expected. But when I do a POST request, the projection won't work. Following the example provided above, "Address" is shown as a URL under Links and is not exposed the way it is with GET request.

How to get it exposed the same way?

I created a class with @RepositoryRestController where I can catch the POST method. If I simply return the entity, it is without links. If I return it as a resource, the links are there, but "Address" is also a link. If I remove the GET method from my controller, the default behavior is as described above.

UPDATE

My entities are same as described here A, B and SuperClass except I don't have fetch defined in my @ManyToOne My controller looks like this:

@RepositoryRestController
public class BRepositoryRestController {

  private final BRepository bRepository;

  public BRepositoryRestController(BRepository bRepository) {
    this.bRepository = bRepository;
  }


  @RequestMapping(method = RequestMethod.POST, value = "/bs")
  public 
  ResponseEntity<?> post(@RequestBody Resource<B> bResource) {
    B b= bRepository.save(bResource.getContent());
    BProjection result = bRepository.findById(b.getId());
    return ResponseEntity.ok(new Resource<>(result));
  }
}

And my repository looks like this:

@RepositoryRestResource(excerptProjection = BProjection.class)
public interface BRepository extends BaseRepository<B, Long> {
  @EntityGraph(attributePaths = {"a"})
  BProjection findById(Long id);
}

And my projection looks like this:

@Projection(types = B.class)
public interface BProjection extends SuperClassProjection {
  A getA();
  String getSomeData();
  String getOtherData();      
}

And SuperClassProjection looks like this:

@Projection(types = SuperClass.class)
public interface SuperClassProjection {
  Long getId();
}

Solution

  • In the custom @RepositoryRestController POST method you should also return the projection. For example:

    @Projection(name = "inlineAddress", types = { Person.class }) 
    public interface InlineAddress {
        String getFirstName();
        String getLastName();
        @Value("#{target.address}") 
        Address getAddress(); 
    }
    
    public interface PersonRepo extends JpaRepository<Person, Long> {
        InlineAddress findById(Long personId); 
    }
    
    @PostMapping
    public ResponseEntity<?> post(...) {
        //... posting a person
        InlineAddress inlineAddress = bookRepo.findById(person.getId());
        return ResponseEntity.ok(new Resource<>(inlineAddress));
    }
    

    UPDATE

    I've corrected my code above and the code from the question:

    @RepositoryRestResource(excerptProjection = BProjection.class)
    public interface BRepository extends CrudRepository<B, Long> {   
      BProjection findById(Long id);
    }
    
    @Projection(types = B.class)
    public interface BProjection {
        @Value("#{target.a}")
        A getA();
        String getSomeData();
        String getOtherData();
    }
    

    Then all works fine.

    POST request body:

    {
        "name": "b1",
        "someData": "someData1",
        "otherData": "otherData",
        "a": {
            "name": "a1"
        }
    }
    

    Response body:

    {
        "a": {
            "name": "a1"
        },
        "someData": "someData1",
        "otherData": "otherData",
        "_links": {
            "self": {
                "href": "http://localhost:8080/api/bs/1{?projection}",
                "templated": true
            }
        }
    }
    

    See working example