I have a very simple JAX-RS service ( BookService class below) that allows you to create objects like Book (also below). POST payload
{ "acquisitionDate": 1418849700000, "name": "Funny Title", "numberOfPages": 100 }
successfully saves the Book and returns 201 CREATED . However, including the id attribute with any value other than zero in the payload, throws an org.hibernate.PersistentObjectException with the message detached entity passed to persist . I understand what this means , and including id in the payload when creating the object (in this case) does not make sense. However, I would prefer to prevent this exception from popping up completely and present to my users, for example, 400 BAD REQUEST in this case (or at least ignore the attribute at all). However, there are two main problems:
- The exception that comes to
create is an EJBTransactionRolledbackException , and I would have to scan the entire path along the stack EJBTransactionRolledbackException to find the root cause; - The main reason is
org.hibernate.PersistentObjectException - I am deploying to Wildfly that uses Hibernate, but I want to keep my code portable, so I really don't want to catch this particular exception.
As far as I understand, there are two possible solutions:
- Use
book.setId(null) to bookRepo.create(book) . This ignores the fact that the id attribute carries a value and executes the request. - Check if
book.getId() != null and throw something like IllegalArgumentException , which can be matched with status code 400 . Seems to be the preferred solution.
However, based on other frameworks (for example, Django Rest Framework, for example), I would prefer it to be handled by the card itself ... Then my question is: is there any built-in way to achieve this behavior that I may be missing?
This is the BookService class:
@Stateless @Path("/books") public class BookService { @Inject private BookRepo bookRepo; @Context UriInfo uriInfo; @Consumes(MediaType.APPLICATION_JSON) @Path("/") @POST @Produces(MediaType.APPLICATION_JSON) public Response create(@Valid Book book) { bookRepo.create(book); return Response.created(getBookUri(book)).build(); } private URI getBookUri(Book book) { return uriInfo.getAbsolutePathBuilder() .path(book.getId().toString()).build(); } }
This is the Book class:
@Entity @Table(name = "books") public class Book { @Column(nullable = false) @NotNull @Temporal(TemporalType.TIMESTAMP) private Date acquisitionDate; @Column(nullable = false, updatable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) @Id private Integer id; @Column(nullable = false) @NotNull @Size(max = 255, min = 1) private String name; @Column(nullable = false) @Min(value = 1) @NotNull private Integer numberOfPages; (getters/setters/...) }
This is the BookRepo class:
@Stateless public class BookRepo { @PersistenceContext(unitName = "book-repo") protected EntityManager em; public void create(Book book) { em.persist(book); } }