2010-01-30 66 views
11

在創建之前防止約束違規檢查的最佳機制是什麼?修改一個實體?在堅持實體之前檢查約束違規

假設如果'User'實體具有'loginid'作爲唯一約束,那麼在創建或修改之前檢查是否存在具有此loginid名稱的用戶條目是否明智?

OR

你會讓數據庫拋出ConstraintViolationException並在UI層妥善處理此消息。應該在jboss seam框架中執行這些檢查。

注意:目前在接縫代碼上沒有執行這樣的檢查。

我們目前在Hibernate中使用Seam 2.2,Richfaces。

回答

6

即使您在保留用戶對象之前檢查了代碼中的條件,在您檢查和保留新用戶之間總會有人創建重複的loginid。

但是,如果您進行明確的檢查,則在UI中顯示適當的錯誤消息會更容易。如果您在捕捉ConstraintViolationException的表上有多個約束,將不允許您輕鬆確定違反了哪個約束。

所以我會做兩個。假設你從Seam的EntityHome延伸:

  1. 在persist()方法中運行查詢以確保loginid是唯一的。如果不是將錯誤消息添加到適當的控件並返回null。
  2. 包裹調用super.persist(),趕上ConstraintViolationException,顯示一個通用的重複錯誤消息

編輯

由於Shervin提到創建JSF Validator是一個好主意(替換) #1以上,但你仍然應該期待最糟糕的,並捕獲ConstraintViolationException。

+0

如果獨立客戶端直接進行JPA調用,您如何防止出現這些問題。我們如何讓代碼在這些場景中重複使用? – Joe 2010-02-02 10:44:06

+0

獨立客戶端是否總是直接進行JPA調用,或者您是否可以強制它通過某種類型的DAO? – mtpettyp 2010-02-08 16:54:39

+0

我們使用Seam組件(EntityHome,EntityQuery接口),它們當前用作用戶界面和JPA持久層之間的粘合劑。 我不清楚,如果我可以強制獨立客戶端直接使用Seam抽象層進行持久化而不是JPA層。如果Seam層必須存在,那麼Seam庫需要作爲客戶端工具包的一部分發貨。 不知道這裏最好的辦法是什麼 – Joe 2010-02-11 03:59:08

2

我的建議是,如果你可以檢查一個條件然後檢查它,即在你的情況下UserExists方法調用。拋出異常是昂貴的,並且用於通常與您不能控制的事情相關的特殊情況。光盤訪問等

在調用實體添加到數據庫之前,您通常會在業務邏輯中執行此檢查。

+1

是的,我唯一關心的是這種方法幾乎不需要爲任何創建或修改記錄而被解僱。我只是在尋找用戶喜歡的最佳方法。謝謝 – Joe 2010-01-30 12:48:19

+1

現在的問題是:什麼是更昂貴的,測試對數據庫的每次寫入操作或僅在出現問題時才處理異常? – Randy 2013-11-02 18:38:29

6

我不同意處理ConstraintException。我寫了一個驗證程序,在保存之前檢查重複項,並且效果很好。

以下是檢查重複電子郵件的示例。

@Name("emailValidator") 
@Validator 
@BypassInterceptors 
@Transactional 
public class UniqueEmailValidator implements javax.faces.validator.Validator, Serializable { 

private static final long serialVersionUID = 6086372792387091314L; 

@SuppressWarnings("unchecked") 
public void validate(FacesContext facesContext, UIComponent component, Object value) throws ValidatorException { 
    EntityManager entityManager = (EntityManager) Component.getInstance("entityManager"); 
    String newEmail = (String) value; 
    String oldEmail = String.valueOf(component.getAttributes().get("oldEmail")); 
    if (oldEmail != null && !oldEmail.equalsIgnoreCase(newEmail)) { 
     List<User> users = entityManager.createQuery(
       "SELECT DISTINCT u FROM " + User.class.getName() + " p where lower(p.fromEmail) = :email").setParameter("email", 
       newEmail.toLowerCase()).getResultList(); 
     if (!users.isEmpty()) { 
      Map<String, String> messages = Messages.instance(); 
      throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.get("admin.emailexists"), messages 
        .get("admin.emailexists"))); 
     } 
    } 
} 
} 

而在你的形式(XHTML)你寫:

<s:decorate template="/layout/definition.xhtml"> 
     <ui:define name="label">#{messages['processdata.email']}</ui:define> 
     <h:inputText id="fromEmail" size="30" required="true" value="# {userAdmin.existingUser.fromEmail}"> 
      <f:validator validatorId="emailValidator"/> 
      <f:attribute name="oldEmail" value="#{userAdmin.existingUser.fromEmail}" /> 
      <s:validate /> 
     </h:inputText> 
    </s:decorate> 

這樣,它總是會在保存之前驗證字段。您甚至可以在焦點更改時放置一個a:支持標籤進行驗證。

+0

如果你的數據庫處理唯一的,那麼它會拋出異常,你的一般異常處理程序將修復它。 – 2010-02-10 19:07:24

+1

仍然有可能在「過程驗證」和「更新模型」JSF階段之間使用重複的電子郵件更新數據庫,並且會拋出ConstraintException。 – mtpettyp 2010-02-10 19:12:43

+0

如果您正在使用樂觀鎖定,即'@ Version',則不太可能發生這種情況 – 2010-12-02 13:00:43