【问题标题】:Deleting an object by its ID and version in JPA在 JPA 中按 ID 和版本删除对象
【发布时间】:2014-10-14 22:34:18
【问题描述】:

我正在学习 JPA,并尝试在 Spring MVC Web 应用程序中使用它。我需要实现一个删除对象/记录的方法。目前,我有以下方法的实现:

@Transactional
public void deleteProduct(int productId, int productVersion) {

    Product product = entityManager.find(Product.class, productId);
    product.setVersion(productVersion);
    entityManager.remove(product);
}

productVersion 用于乐观锁定。它是来自 Web GUI 的对象/记录的旧版本。

该方法删除DB中的记录,但是当DB中记录的版本与productVersion不匹配时不会抛出异常。 (我只有 删除 对象有问题:当我 更新 带有 entityManager.merge(product) 的记录时,我收到一条异常消息:Row was updated or deleted by another transaction。)

Hibernate 生成以下 SQL 查询:delete from Product where id=? and version=?,即它会尝试检查 version 字段。

我做错了什么?

另外,通过 id 删除对象是正确的方法吗?我担心我的方法生成了两个 SQL 查询:SELECT 用于entityManager.find()DELETE 用于entityManager.remove()。有没有更优化的删除对象/记录的方法?

产品类别

@Entity
public class Product {

    @Id
    @GeneratedValue
    protected int id;

    protected String name;

    protected BigDecimal price;

    @Version
    protected int version;

    // getters and setters
}

【问题讨论】:

  • 您的问题标签表明您正在使用 Hibernate 和 Spring。那是对的吗?如果是这样,您为什么不使用会话工厂之类的东西让您的 ORM 生活更轻松?
  • 你想要实现什么行为?您希望删除始终成功还是仅在版本匹配时才成功删除?
  • @MrPixelDream 是的,我使用 Hibernate 和 Spring。我使用 JPA,因为它是 Java EE 的一部分。我无法切换到纯 Hibernate。
  • @VarunPhadnis 当前方法总是删除记录。我希望它在版本不匹配时抛出异常。

标签: spring hibernate spring-mvc jpa


【解决方案1】:

一种方法是在一个查询中手动执行,如下所示:

@Transactional
public void deleteProduct(int productId, int productVersion) {
   int isSuccessful = entityManager.createQuery("delete from Product p where p.productVersion=:productVersion and p.id=:id")
            .setParameter("id", productId)
            .setParameter("productVersion", productVersion)
            .executeUpdate();
    if (isSuccessful == 0) {
        throw new OptimisticLockException(" product modified concurrently");
    }
}

【讨论】:

  • +1 谢谢!您的代码有效,它只生成一个 SQL 查询。但我的例子(大部分)只是一个学习练习。之前多次使用SQL查询,现在想了解一下ORM的做事方式。我将保留您的代码作为最后的手段,但会等待其他答案。
  • 我还查看了您在之前版本的答案中提到的 entityManager.getReference() 方法。从this 的帖子来看,这确实是一种避免生成第二个 SQL 查询 (SELECT) 的方法。但在我的应用程序中,SELECT 无论如何都会出于某种原因生成。
  • 使用 entityManager.getReference() 在您的情况下仍会生成两个查询,因为您将尝试在实体 (product.setVersion(productVersion)) 中设置 productVersion 从而强制您的 JPA 提供程序加载整个实体而不是使用代理。我之前已经发布过,假设您要始终删除(忽略 productVersion)。希望这会有所帮助。
  • 这个答案的另一个问题是它不执行级联。在很多情况下,这种解决方案是不够的。我想实现这个方法的通用版本(在 Spring Data JPA 中),我最终得到:查找、手动检查版本并删除。或者,合并 + 删除可能有效,但我没有检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-22
  • 2018-11-13
  • 2017-01-04
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
  • 1970-01-01
相关资源
最近更新 更多