JPA @Version 合并行为不适用于 EclipseLink
JPA @Version merge Behavior not working for EclipseLink
当我将一个简单的项目部署到默认使用休眠的 WildFly 9.0.1 服务器时,一切正常。但是,部署到默认使用 EclipseLink 的 Glassfish 4.1(或 Payara)服务器会导致我的集成测试失败。
具体来说,当部署在 Glassfish 而非 WildFly 上时需要插入时,EntityManager.merge 方法会导致版本从 0 变为 1。
我的集成测试正在为每个测试件调用 JAX-RS 服务。 ToDoManager 中的每个方法都是一个 JPA 事务,所以如果我没记错的话,我不需要刷新数据库来更新。
用于捕获 EJBExeption 的 JAX-RS ExceptionMapper:
@Provider
public class EJBExceptionMapper implements ExceptionMapper<EJBException> {
@Override
public Response toResponse(EJBException ex) {
Throwable cause = ex.getCause();
Response unknownError = Response.serverError().
header("cause", ex.toString()).build();
if (cause == null) {
return unknownError;
}
if (cause instanceof OptimisticLockException) {
return Response.status(Response.Status.CONFLICT).
header("cause", "conflict occured: " + cause).
build();
}
return unknownError;
}
}
创建和更新请求的保存端点:
@Stateless
@Path("todos")
public class TodosResource {
@Inject
ToDoManager manager;
... other REST Calls ...
@POST
public Response save(@Valid ToDo todo, @Context UriInfo info) {
ToDo saved = this.manager.save(todo);
long id = saved.getId();
URI uri = info.getAbsolutePathBuilder().path("/"+id).build();
return Response.created(uri).build();
}
}
底层持久性:
@Stateless
@Interceptors(BoundaryLogger.class)
public class ToDoManager {
@PersistenceContext
EntityManager em;
... other persistence methods ...
public ToDo save(ToDo todo) {
return this.em.merge(todo);
}
}
当部署在 Glassfish 上进行 first 更新尝试时,以下测试将抛出 OptimisticLockException 并且测试在应该通过时失败。而当部署到 WildFly 时,第一次更新发生,测试通过,然后第二次测试通过,因为我正在检查 409 状态代码。
测试:
@Test
public void crud() {
// build a JSON ToDo
JsonObjectBuilder todoBuilder = Json.createObjectBuilder();
JsonObject todoToCreate = todoBuilder.
add("caption", "implement").
add("priority", 10).
build();
// Run Create Test
Response postResponse = this.provider.target().request().
post(Entity.json(todoToCreate));
assertThat(postResponse.getStatus(), is(201));
String location = postResponse.getHeaderString("Location");
System.out.println("location = " + location);
// Run Find Test
JsonObject dedicatedTodo = this.provider.client().
target(location).
request(MediaType.APPLICATION_JSON).
get(JsonObject.class);
assertTrue(dedicatedTodo.getString("caption").contains("implement"));
// Run Update Test
JsonObjectBuilder updateBuilder = Json.createObjectBuilder();
JsonObject updated = updateBuilder.
add("id", dedicatedTodo.getInt("id")).
add("caption", "implemented").
add("priority", 10).
add("version", dedicatedTodo.getInt("version")).
build();
Response updateResponse = this.provider.client().
target(location).
request(MediaType.APPLICATION_JSON).
put(Entity.json(updated));
assertThat(updateResponse.getStatus(), is(200));
// Run Update Test Again for Lock Testing
updateBuilder = Json.createObjectBuilder();
updated = updateBuilder.
add("id", dedicatedTodo.getInt("id")).
add("caption", "implemented").
add("priority", 8).
add("version", dedicatedTodo.getInt("version")).
build();
updateResponse = this.provider.client().
target(location).
request(MediaType.APPLICATION_JSON).
put(Entity.json(updated));
assertThat(updateResponse.getStatus(), is(409));
String conflictInformation = updateResponse.getHeaderString("cause");
assertNotNull(conflictInformation);
System.out.println("conflictInformation = " + conflictInformation);
当我在 Glassfish 上创建后查看 @Version private long version
值时,该值为 1,导致第一次更新失败。但是,在 WildFly 上,它仍然为 0,因此允许进行第一次更新。
由于我已经启动了生产 Payara Server 并且 运行,并且我想在未来的项目中使用这种方法,任何帮助解释我如何使它在 Glassfish 上工作的帮助都将非常有用有帮助。
没有足够的代码来解决正在发生的事情。这将取决于刷新到数据库时的事务边界 and/or。我无法从代码中找出事务边界是什么。当通过显式刷新或事务提交将对象写入数据库时,版本字段应递增。
在您的保存方法中,您可以尝试执行 em.flush 以查看两个提供程序之间的行为是否一致。
您可以通过执行以下操作来确保起始值一致;
@Version
private int version = 1;
这应该是显而易见的,但是,我正在关注一个研讨会,而该研讨会不包括以下内容。
测试期间的更新调用需要正在编辑的对象的 ID 和版本。添加此信息后,第一次更新成功,第二次更新失败并出现预期的 OptimisticLockException,从而通过了测试。
当我将一个简单的项目部署到默认使用休眠的 WildFly 9.0.1 服务器时,一切正常。但是,部署到默认使用 EclipseLink 的 Glassfish 4.1(或 Payara)服务器会导致我的集成测试失败。
具体来说,当部署在 Glassfish 而非 WildFly 上时需要插入时,EntityManager.merge 方法会导致版本从 0 变为 1。
我的集成测试正在为每个测试件调用 JAX-RS 服务。 ToDoManager 中的每个方法都是一个 JPA 事务,所以如果我没记错的话,我不需要刷新数据库来更新。
用于捕获 EJBExeption 的 JAX-RS ExceptionMapper:
@Provider
public class EJBExceptionMapper implements ExceptionMapper<EJBException> {
@Override
public Response toResponse(EJBException ex) {
Throwable cause = ex.getCause();
Response unknownError = Response.serverError().
header("cause", ex.toString()).build();
if (cause == null) {
return unknownError;
}
if (cause instanceof OptimisticLockException) {
return Response.status(Response.Status.CONFLICT).
header("cause", "conflict occured: " + cause).
build();
}
return unknownError;
}
}
创建和更新请求的保存端点:
@Stateless
@Path("todos")
public class TodosResource {
@Inject
ToDoManager manager;
... other REST Calls ...
@POST
public Response save(@Valid ToDo todo, @Context UriInfo info) {
ToDo saved = this.manager.save(todo);
long id = saved.getId();
URI uri = info.getAbsolutePathBuilder().path("/"+id).build();
return Response.created(uri).build();
}
}
底层持久性:
@Stateless
@Interceptors(BoundaryLogger.class)
public class ToDoManager {
@PersistenceContext
EntityManager em;
... other persistence methods ...
public ToDo save(ToDo todo) {
return this.em.merge(todo);
}
}
当部署在 Glassfish 上进行 first 更新尝试时,以下测试将抛出 OptimisticLockException 并且测试在应该通过时失败。而当部署到 WildFly 时,第一次更新发生,测试通过,然后第二次测试通过,因为我正在检查 409 状态代码。
测试:
@Test
public void crud() {
// build a JSON ToDo
JsonObjectBuilder todoBuilder = Json.createObjectBuilder();
JsonObject todoToCreate = todoBuilder.
add("caption", "implement").
add("priority", 10).
build();
// Run Create Test
Response postResponse = this.provider.target().request().
post(Entity.json(todoToCreate));
assertThat(postResponse.getStatus(), is(201));
String location = postResponse.getHeaderString("Location");
System.out.println("location = " + location);
// Run Find Test
JsonObject dedicatedTodo = this.provider.client().
target(location).
request(MediaType.APPLICATION_JSON).
get(JsonObject.class);
assertTrue(dedicatedTodo.getString("caption").contains("implement"));
// Run Update Test
JsonObjectBuilder updateBuilder = Json.createObjectBuilder();
JsonObject updated = updateBuilder.
add("id", dedicatedTodo.getInt("id")).
add("caption", "implemented").
add("priority", 10).
add("version", dedicatedTodo.getInt("version")).
build();
Response updateResponse = this.provider.client().
target(location).
request(MediaType.APPLICATION_JSON).
put(Entity.json(updated));
assertThat(updateResponse.getStatus(), is(200));
// Run Update Test Again for Lock Testing
updateBuilder = Json.createObjectBuilder();
updated = updateBuilder.
add("id", dedicatedTodo.getInt("id")).
add("caption", "implemented").
add("priority", 8).
add("version", dedicatedTodo.getInt("version")).
build();
updateResponse = this.provider.client().
target(location).
request(MediaType.APPLICATION_JSON).
put(Entity.json(updated));
assertThat(updateResponse.getStatus(), is(409));
String conflictInformation = updateResponse.getHeaderString("cause");
assertNotNull(conflictInformation);
System.out.println("conflictInformation = " + conflictInformation);
当我在 Glassfish 上创建后查看 @Version private long version
值时,该值为 1,导致第一次更新失败。但是,在 WildFly 上,它仍然为 0,因此允许进行第一次更新。
由于我已经启动了生产 Payara Server 并且 运行,并且我想在未来的项目中使用这种方法,任何帮助解释我如何使它在 Glassfish 上工作的帮助都将非常有用有帮助。
没有足够的代码来解决正在发生的事情。这将取决于刷新到数据库时的事务边界 and/or。我无法从代码中找出事务边界是什么。当通过显式刷新或事务提交将对象写入数据库时,版本字段应递增。
在您的保存方法中,您可以尝试执行 em.flush 以查看两个提供程序之间的行为是否一致。
您可以通过执行以下操作来确保起始值一致;
@Version
private int version = 1;
这应该是显而易见的,但是,我正在关注一个研讨会,而该研讨会不包括以下内容。
测试期间的更新调用需要正在编辑的对象的 ID 和版本。添加此信息后,第一次更新成功,第二次更新失败并出现预期的 OptimisticLockException,从而通过了测试。