JPA可更新属性和JPQL

时间:2011-10-16 17:55:00

标签: hibernate jpql

我在测试中提出了一些意见,我发现他们很难理解。我的测试对一个实体执行一些基本的更新(或合并)操作,该实体的一个属性设置为updatable = false。


@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注释,版本号也不会增加。我理解这种行为可以通过在更新查询中添加“versioned”关键字来实现,但我想了解执行JPQL查询时不会更新版本属性背后的技术推理。

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

1 个答案:

答案 0 :(得分:2)

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

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

  3. AFAIK,版本号只有在使用乐观锁定时才会增加,因此在使用JPQL时,您必须指定是否确实要增加它。

  4. 示例:

    场景1:您希望获得员工权利,然后进行修改。如果您实际对Employee实体进行了一些修改,那么只有在您第一次读取它并且在您提交修改时间之间没有其他人对同一实体进行某些修改时,您希望这些修改保持不变。为此,您可以像这样阅读您的实体:

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

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

    场景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;
    }