不应该将EntityManager#与不可识别的实体合并吗?

时间:2016-12-23 14:36:11

标签: jpa merge jax-rs entitymanager

我正在设计JAX-RS API。

POST /myentities
PUT  /myentities/{myentityId}

我确实喜欢这个。

@PUT
@Path("/{myentityId: \\d+}")
@Consumes(...)
@Transactional
public Response updateMyentity(
    @PathParam("myentityId") final long myentityId,
    final Myentity myentity) {
    if (!Objects.equal(myentitiyId, myentity.getId())) {
        // throw bad request
    }
    entityManager.merge(myentity);
    return Response.noContent().build();
}

我突然好奇并向自己提出质疑。

当客户调用以下请求时

PUT /myentities/101 HTTP/101
Host: ..

<myentity id=101>
</myentity>

即使没有101标识的资源,是否可以处理请求?

我跑了一个测试。

acceptEntityManager(entityManager -> {
    // persist
    final Device device1 = mergeDevice(entityManager, null);
    entityManager.flush();
    logger.debug("device1: {}", device1);
    assertNotNull(device1.getId());
    // remove
    entityManager.remove(device1);
    entityManager.flush();
    assertNull(entityManager.find(Device.class, device1.getId()));
    // merge again, with non existing id
    final Device device2 = entityManager.merge(device1);
    entityManager.flush();
    logger.debug("device2: {}", device2);
});

我发现第二次合并工作并分配了新的ID。 这是正常的吗?难道不会EntityManager#merge否认这次行动吗?

是否意味着任何客户端都可以通过调用

来攻击API
PUT /myentities/<any number>

1 个答案:

答案 0 :(得分:0)

如果我理解这一点,那么是的,这就是合并应该如何运作。 merge创建实体类的新实例,复制您提供的实体的当前状态(在您的情况下为device1),并将该新实例添加到事务中。 因此,管理NEW实例,并且您对旧实例所做的任何更改都不会在事务中更新,也不会通过flush - 操作传播到数据库。

因此,当您使用device1从数据库中删除entityManager.remove(device1);时,呼叫为 final Device device2 = entityManager.merge(device1);将新的副本(带有新ID)添加到数据库中。您对device1所做的更改不会在事务中进行跟踪,因此一旦您flush,它们就不会反映在数据库表中。另一方面,将在交易中跟踪对device2的更改。