Guilherme Cardoso
Guilherme Cardoso

Reputation: 15

Spring Data JPA and PUT requests to create

So I'm trying to use Spring Data JPA to make some rest services using the repository interfaces. But I'm stuck trying to do something without having to create a custom controller.

Suppose this service only accepts PUT and GET requests. The PUT requests are used to create and update the resource. So the ID is generated client side.

The entity and repository would be something like this:

@Entity
public class Document {
    @Id
    private String name;
    private String text;
        //getters and setters
}

@RepositoryRestResource(collectionResourceRel = "documents", path = "documents")
public interface DocumentRepository extends PagingAndSortingRepository<Document, String> {
}

When I try to make a PUT request @ localhost:8080/documents/foo with the following body:

{ 
  "text": "Lorem Ipsum dolor sit amet"
}

I get this message:

{
  "timestamp": 1474930016665,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "org.springframework.orm.jpa.JpaSystemException",
  "message": "ids for this class must be manually assigned before calling save(): hello.Document; nested exception is org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): hello.Document",
  "path": "/documents/foo"
}

So I have to send in the body:

{ 
  "name": "foo",
  "text": "Lorem Ipsum dolor sit amet"
}

so it returns 201 Created with

{
  "text": "Lorem Ipsum dolor sit amet",
  "_links": {
    "self": {
      "href": "http://localhost:8080/documents/foo"
    },
    "document": {
      "href": "http://localhost:8080/documents/foo"
    }
  }
}

Is it possible to make the PUT without having to send the id (the name field) inside json body? Since I'm already sending it in the URI?

I know that I can create a RestController and some requestmapping with a /documents/{document.name}, and use it to set the name field before saving it, but I wanted to know if there is any annotation or something.

Upvotes: 1

Views: 2095

Answers (1)

alexbt
alexbt

Reputation: 17065

You could define an @HandleBeforeCreate / @HandleBeforeSave method to alter the model just before saving it:

@Component
@RepositoryEventHandler(Document.class)
public class DocumentBeforeSave {
    @Autowired
    private HttpServletRequest req;

    @HandleBeforeCreate
    public void handleBeforeSave(Document document) {
        if("PUT".equals(req.getMethod())){
            String uri = req.getRequestURI();
            uri = uri.substring(uri.lastIndexOf('/') + 1);
            document.setName(uri);
        }
    }
}
  • Because the body does not contain any id (at this point), both POST and PUT will trigger the @HandleBeforeCreate method (if the body contained an id, a PUT request would rather trigger the @HandleBeforeSave).
  • We need to check if the RequestMethod is PUT before assigning the id (in order to leave the POST body unchanged).
  • The HttpServletRequest is injected as a proxy and can be used by multiple threads. Read this : Can't understand `@Autowired HttpServletRequest` of spring-mvc well

Upvotes: 2

Related Questions