2011-10-16 92 views
0

我在測試中做了幾個觀察,我發現它們很難理解。我的測試對一個實體進行了一些基本的更新(或合併)操作,該實體的屬性設置爲updatable = false。JPA可更新屬性和JPQL

 

@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. 上述員工實體的name屬性更新=「假」集。如果我加載並設置一個僱員實體的名稱屬性,然後調用entityManager.merge(employee),合併不會執行任何更新查詢,這是預期的行爲,但它也不會拋出任何異常。是否可以配置JPA,以便引發運行時異常?

  2. 而不是調用entityManager.merge(employee),如果使用JPQL查詢更新name屬性,則查詢將成功執行並更新name屬性。 JPQL查詢行爲背後的技術推理與JPA對象更新有何不同,爲什麼?

  3. 如果使用JPQL查詢執行更新,儘管使用@Version註釋,版本號仍不會增加。我明白,這種行爲可以通過在更新查詢中添加「versioned」關鍵字來實現,但我希望瞭解在執行JPQL查詢時版本屬性背後的技術推理不會被更新。

由於JPQL是一種面向對象的查詢語言(如HQL是),我覺得不應該有對點2和3的技術實現問題這將是偉大聽到一些這方面的專家意見。謝謝。

+0

我面對有關合並了同樣的問題。我沒有找到這個問題,所以我有另一個開放[問題](http://stackoverflow.com/questions/15108064/unexpected-behaviour-of-entitymanager-merge)。我發現的主要問題是合併返回一個對象,它永遠不會持久,也永遠不會持久。我正在爲此提出一個錯誤。 -Atul –

回答

2
  1. 那麼規範簡單地說,當一個列是updateable =「false」時,它應該被生成的SQL忽略。對拋出異常沒有任何說法。但是,您可以使用偵聽器來實現該功能。只需在你的bean中聲明一個類似於「checkModif」的方法,用​​對它進行註釋,並且如果該字段被修改則拋出異常(注意但是這將有效地取消操作並且什麼都不會更新)。更可能是一個Hibernate的bug。 JPA2規範簡單地說,當一個字段被註釋爲@Column(name = "EMP_NAME", updatable = false)時,相應的列應該被提供者生成的SQL忽略。當SQL來自JPQL時,不會說特定情況。在Hibernate的JPA2實現中有很多像這樣的「事故」(比如不能使用地圖關聯中的關鍵字進行搜索),AFAIK僅在使用樂觀鎖定時增加版本號,因此在使用JPQL時,您必須指定,如果你真的想增加與否

例:

方案1:如果您想獲得一個員工entitity,然後修改它,如果你確實做了一些修改,以Employee實體,你希望這些修改只有在沒有人做出修改的情況下才會持續ns在你第一次閱讀它的時間和你提交修改的時間之間到同一個實體。爲了做到這一點,你看你的實體是這樣的:

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; 
} 
+0

你能詳細說明第3點嗎?當我添加@Version註釋我實際上是讓hibernate使用樂觀鎖定權?所以我不明白在hql/jpql查詢中指定「版本化」關鍵字的必要性。 –

+0

不,當你添加版本時,你不會告訴Hibernate的JPA實現使用樂觀鎖。要做到這一點,你要麼使用EntityManager或查詢對象(它有方法,如setLockType等)。請閱讀以下內容:http://download.oracle.com/javaee/6/tutorial/doc/gkjhz.html和以下章節瞭解如何使用鎖定和工作的詳細信息。 –

+0

好的。正如上面的鏈接所言:「通過添加一個版本屬性,該實體啓用了樂觀併發控制。」那麼你是否指出持久化提供者(hibernate)的JPQL實現不會查看實體屬性?我這樣說是因爲上面的僱員實體已經擁有@version屬性,因此我期望JPQL查詢增加版本,因爲我正在使用樂觀鎖定。 –