我有一个非常基本的 JAX-RS 服务(BookService
下面的类)允许创建类型的实体Book
(也见下文)。POST
有效负载
{
"acquisitionDate": 1418849700000,
"name": "Funny Title",
"numberOfPages": 100
}
成功地坚持了Book
并返回201 CREATED
。然而,包括一个id
有效负载上具有任何非空值的属性都会触发org.hibernate.PersistentObjectException
与消息detached entity passed to persist
. 我明白这意味着什么,并包括一个id
创建对象时(在本例中)在有效负载上没有任何意义。但是,我更愿意防止此异常一直冒泡,并向我的用户展示,例如400 BAD REQUEST
在这种情况下(或者至少完全忽略该属性)。然而,有两个主要问题:
- 到达的异常
create
is an EJBTransactionRolledbackException
我必须沿着堆栈跟踪一路爬行才能发现根本原因;
- 根本原因是
org.hibernate.PersistentObjectException
- 我正在部署到使用 Hibernate 的 Wildfly,但我想保持我的代码可移植,所以我真的不想捕获这个特定的异常。
据我了解,有两种可能的解决方案:
- Use
book.setId(null)
before bookRepo.create(book)
。这会忽略这样一个事实:id
属性携带一个值并继续请求。
- 检查是否
book.getId() != null
并抛出类似的东西IllegalArgumentException
可以映射到400
状态码。似乎是更好的解决方案。
然而,来自其他框架(例如 Django Rest Framework),我真的更喜欢由框架本身来处理这个问题...我的问题是,是否有任何内置方法可以实现我可以实现的这种行为失踪了吗?
这是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();
}
}
这是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/...)
}
这是BookRepo
class:
@Stateless
public class BookRepo {
@PersistenceContext(unitName = "book-repo")
protected EntityManager em;
public void create(Book book) {
em.persist(book);
}
}