【问题标题】:JPA updatable attribute and JPQLJPA 可更新属性和 JPQL
【发布时间】:2011-12-08 19:51:10
【问题描述】:

我在测试中做了一些观察,我发现它们很难理解。我的测试对一个实体执行一些基本的更新(或合并)操作,该实体的一个属性设置为可更新=假。

@Entity @Table(name = "EMPLOYEE") public class Employee { @Id @GeneratedValue(generator = "some-generator") @Column(name = "EMP_ID", nullable = false) private Long id; @Column(name = "EMP_NAME", updatable = false) private String name; @Version @Column(name = "OBJECT_VERSION") private int version; public Employee() { } public Employee(String name) { this.name = name; } public void setName(String name) { this.name = name; } }

注意 - 我使用 Hibernate 作为我的持久性提供程序。

我的观察和疑惑是

  1. 上述 Employee 实体的 name 属性设置了 updatable="false"。如果我加载并设置员工实体的名称属性,然后调用 entityManager.merge(employee),则合并不会执行任何更新查询,这是预期的行为,但它也不会引发任何异常。是否可以配置 JPA 以引发运行时异常?

  2. 如果使用 JPQL 查询更新 name 属性,而不是调用 entityManager.merge(employee),则查询将成功执行并且 name 属性得到更新。 JPQL 查询行为与 JPA 对象更新不同的技术原因是什么?为什么?

  3. 如果使用 JPQL 查询执行更新,则版本号不会增加,尽管有 @Version 注释。我知道可以通过在更新查询中添加“版本化”关键字来实现此行为,但我想了解执行 JPQL 查询时版本属性不会更新的技术原因。

由于 JPQL 是一种面向对象的查询语言(就像 HQL 一样),我认为第 2 点和第 3 点不应该存在技术实现问题。很高兴听到一些专家 cmet 对此进行讨论。谢谢。

【问题讨论】:

  • 我在合并时遇到了同样的问题。我没有找到这个问题,所以我再打开一个question。我发现的主要问题是合并返回一个对象,该对象永远不会持续存在,也永远不会持续存在。我正在提交一个关于此的错误。 -阿图尔

标签: hibernate jpql


【解决方案1】:
  1. 好吧,规范只是说当列是可更新的=“假”时,生成的 SQL 应该忽略它。没有说任何关于抛出异常的事情。但是,您可以使用侦听器来实现它。只需在 bean 中声明一个方法,如“checkModif”,用 @PreUpdate 注释它,如果字段被修改,则抛出异常(但是请注意,这将有效地取消操作,并且不会更新任何内容)

  2. 这更可能是 Hibernate 错误。 JPA2 规范简单地说,当一个字段用@Column(name = "EMP_NAME", updatable = false) 注释时,相应的列应该被提供者生成的 SQL 忽略。当该 SQL 来自 JPQL 时,没有提及特定情况。在 Hibernate 的 JPA2 实现中有很多这样的小“事故”(比如无法使用地图关联中的键进行搜索

  3. AFAIK,版本号只有在使用乐观锁定时才应该增加,所以在使用 JPQL 时,您必须指定是否真的要增加它。

例子:

场景 1:您想要获取一个 Employee 实体,然后对其进行修改。如果您实际上对 Employee 实体进行了一些修改,那么您希望这些修改只有在您第一次阅读它和提交修改之间的时间里没有其他人也对同一实体进行一些修改时才保留。为此,您可以像这样读取您的实体:

Query query = entityManager.createQuery("SELECT e FROM EMPLOYEE e where e.id=3");
query.setLockMode(LockModeType.OPTIMISTIC);
Employee e = query.getSingleResult();

这样,如果其他用户在您修改它时使用相同的上述代码来读取相同的实体,并且这个新人比您快并且比您更快地提交修改,那么如果您自己修改某些东西并且尝试提交你会得到一个 OptimisticLockException 并且你不会提交你的修改,因此不会覆盖其他人所做的(此时你不知道的)修改。

场景 2。您希望确保读取一个实体,并且在保存它时,即使您没有修改其中的任何内容,您也希望仅在没有其他人修改任何其他内容的情况下保留它(使用相同的值)平均时间也是如此。为此,您可以这样做

Query query = entityManager.createQuery("SELECT e FROM EMPLOYEE e where e.id=3");
query.setLockMode(LockModeType.OPTIMISTIC_FORCE_INCREMENT);
//etc etc...

在这两种情况下,Employee 实体都必须有一个版本化的字段才能工作:

@Version
protected int version;

public int getVersion() {
    return version;
}

public void setVersion(int version) {
    this.version = version;
}

【讨论】:

  • 您能详细说明第 3 点吗?当我添加@Version 注释时,我实际上是在告诉休眠使用乐观锁定对吗?所以我不明白在 hql/jpql 查询中指定“版本化”关键字的必要性。
  • 不,当您添加版本控制时,您并没有告诉 Hibernate 的 JPA 实现使用乐观锁。为此,您可以使用 EntityManager 或 Query 对象(具有 setLockType 等方法)。请阅读:download.oracle.com/javaee/6/tutorial/doc/gkjhz.html 和下一章,了解有关锁的使用和工作方式的详细信息。
  • 好的。正如上面的链接所说“通过添加版本属性,实体启用了乐观并发控制。”。那么,您是否表示持久性提供程序(休眠)的 JPQL 实现不会查看实体属性?我这样说是因为上面的员工实体已经具有 @version 属性,因此我希望 JPQL 查询增加版本,因为我使用的是乐观锁定。
  • 查看我最初的回答。我已经用代码示例更新了它。
  • 感谢您提供详细示例。基本上我们是说,在使用 JPQL 或 HQL 时,我们需要显式设置锁定模式(除了在模型中定义版本属性)来告诉底层持久性提供应该使用乐观锁定。当我们处理 entityManager(在 JPA 的情况下)或休眠会话时,这不是必需的。
猜你喜欢
  • 2011-07-07
  • 1970-01-01
  • 2013-04-01
  • 2019-01-07
  • 1970-01-01
  • 2018-03-30
  • 2015-07-15
  • 2015-12-15
  • 1970-01-01
相关资源
最近更新 更多