2014-02-17 45 views
2

我遇到了一個奇怪的問題,每當我嘗試在應用程序中編輯一個寄存器而不是更新實體的狀態時,它會持續一個新的問題。這裏不用我update方法JPA堅持而不是更新

public void update(Object obj) { 
    EntityManagerFactory fac = Persistence.createEntityManagerFactory("crud"); 
    EntityManager em = fac.createEntityManager(); 
    em.merge(obj); 
    em.getTransaction().begin(); 
    em.getTransaction().commit(); 
    em.close(); 
    fac.close(); 
} 

我的猜測是,當我提交編輯表單和setter方法被稱爲Id以某種方式設置爲0,這有可能發生嗎?該形式如下:

<h:form> 
       <p:panelGrid columns="2" style="margin: 0 auto;"> 
        <f:facet name="header"> 
         Edit form 
        </f:facet> 
        Name: 
        <h:inputText id="nome" value="#{editUserBean.userToEdit.name}"/> 
        Age: 
        <h:inputText value="#{editUserBean.userToEdit.age}" converter="javax.faces.Integer"/> 
        Pass: 
        <h:inputText value="#{editUserBean.userToEdit.password}"/> 
        Gender: 
        <h:selectOneRadio value="#{editUserBean.userToEdit.gender}" style="font-size: 12px;"> 
         <f:selectItem itemValue="Masc" itemLabel="Masc" /> 
         <f:selectItem itemValue="Fem" itemLabel="Fem" /> 
        </h:selectOneRadio> 
        <f:facet name="footer"> 
         <div align="center"> 
          <h:commandButton value="save" actionListener="#{editUserBean.save}" icon="ui-icon-check"/> 
         </div> 
        </f:facet> 
       </p:panelGrid> 
</h:form> 

Hibernate在查詢此:

Hibernate: 
    select 
     hibernate_sequence.nextval 
    from 
     dual 
Hibernate: 
    insert 
    into 
     users 
     (age, gender, name, password, id) 
    values 
     (?, ?, ?, ?, ?) 
+0

爲什麼你要創建entityManager?它應該爲每個實例創建一次(所有會話都在靜態上下文中進行)。而不是合併,你只需要堅持。如果你添加了新的狀態,EntityManager將映射已經存在的對象並更新它。但是,如果每次要更新時都創建entityManager,則無法映射已保存的對象。 – solvator

+0

其實我把它放在那裏作爲上下文的名字,我的EntityManager是一個全局變量 –

+0

所以你可以在靜態{}塊中創建它。 – solvator

回答

1

實際的問題是在JSF層。

所有證據表明editUserBean請求範圍。這意味着您將爲每個請求獲得一個新的bean。

因此,我們正在爲每個請求使用一個新的userToEdit bean。其屬性將爲null或基元的默認值(例如,0intlong),除非您明確聲明瞭bean內屬性的默認值。這包括id,您的情況是0

當您合併您的bean時,持久性上下文中沒有id爲0的實體或數據庫中的相應條目,因此merge將持久存在一個新實體。如果您爲主鍵生成值,則可能不會有0作爲id,所以JPA圖層將保持持久化新實體。

此問題的一個簡單的解決在於存儲在一個bean與broader scope的用戶信息,說會話範圍或用戶定義對話範圍

@ConversationScoped 
class UserToEdit extends User { 
    // ... or if you want, create a HAVE-A relationship 
} 

然後你可以注入你的bean作爲通常和存儲的id將在請求之間生存(不要忘記清理;正確劃分會話邊界和/或清理會話bean)。


另一個解決方法 - 如果你絕對必須保留用戶信息的請求範圍 - 使用隱藏字段來保存id的(你希望以及任何不可編輯的屬性)值。這樣用戶id也將在下一次請求時提交,模型將被正確更新。

<h:inputHidden value="#{editUserBean.userToEdit.id}" /> 

警告:雖然這個解決辦法是非常受歡迎的,只有這樣做,如果當前用戶是能夠更新任何其他用戶的信息。爲什麼?惡意用戶可以輕鬆操縱DOM以更改user.id和/或提交包含任何id的虛假請求來濫用系統。

0

我不知道你是否已經嘗試過這一點,但檢查Hibernate配置文件......然後可能是一個的標籤包含值創建。將其更改爲更新。希望這有助於

+0

是的嘗試,可能每次你創建EMF你擦除數據,確保配置文件表示驗證不創建。我認爲這可能是問題,非常好的建議,我的朋友,如果它的工作考慮+1 – Koitoer

+0

不,我正在使用persistence.xml。屬性hibernate.hbm2ddl.auto被設置爲更新 –

1

夫婦的事情...首先注入你這樣的PersistenceContext到EJB:

@PersistenceContext 
private EntityManager em; 

接下來,標誌着你的函數,或者用適當的自動交易處理整個EJB:

@Stateless 
@TransactionAttribute(TransactionAttributeType.REQUIRED) 
public class MyServiceEJBThingy { 
    @PersistenceContext 
    private EntityManager em; 

在你的方法中,除了業務邏輯之外別做任何事情。現在交易語義學對正在爲你自動處理的:

@Stateless 
@TransactionAttribute(TransactionAttributeType.REQUIRED) 
public class MyServiceEJBThingy { 
    @PersistenceContext 
    private EntityManager em; 

    public void update(Object obj) { 
     //GUI independent business logic here 
     em.merge(obj); 
    } 
} 

最後,現在在您的視圖控制器注入你的EJB(假設你使用MVVM模式):

@RequestScoped (or something) 
public class EditUserBean { 
    @Inject 
    private MyServiceEJBThingy myServiceEJBThingy; 


    public void update(Object obj) { 
     // GUI logic here 
     myServiceEJBThingy.update(obj); 
    } 
} 

簡單多了是嗎? :) Java可以非常簡單和高效。我不確定你使用的是什麼容器,但我建議使用Apache TomEE。看看他們的大型圖書館最新的例子here。祝你好運!!!

+0

去實現它。謝謝 –

+0

如果有效,請不要忘記註冊和/或接受我的回答和其他人的回答;)這就是你如何獎勵幫助你在計算器上的人:) –

+0

一旦我有信譽upvote,當然:) –