2010-01-30 10 views
11

Jaki jest najlepszy mechanizm zapobiegania naruszaniu ograniczeń przed utworzeniem | modyfikacja podmiotu?sprawdza naruszenie ograniczenia przed utrwaleniem podmiotu

Załóżmy, że jeśli jednostka "użytkownika" ma "loginid" jako ograniczenie unikalności, czy byłoby rozsądnie sprawdzić, czy istnieje wpis użytkownika o tej nazwie identyfikatora przed utworzeniem lub modyfikacją.

LUB

Pozwolisz baza rzucić ConstraintViolationException i obsługi tego komunikatu odpowiednio w warstwie UI. Gdzie takie kontrole powinny być egzekwowane w ramach jboss.

Uwaga: Obecnie takie kontrole nie są wymuszane na kodzie szwu.

Obecnie używamy Seam 2.2, Richfaces z Hibernate.

Odpowiedz

6

Nawet jeśli sprawdzisz warunek w kodzie przed utrwaleniem obiektu użytkownika, zawsze istnieje szansa, że ​​ktoś utworzy duplikat identyfikatora logowania między czasem sprawdzenia a utrzymaniem nowego użytkownika.

Jednak łatwiej będzie wyświetlić odpowiedni komunikat o błędzie w interfejsie użytkownika, jeśli wykonasz wyraźne sprawdzenie. Jeśli w tabeli występuje wiele ograniczeń, wyjątek ConstraintViolationException nie pozwoli na łatwe określenie, które ograniczenie zostało naruszone.

Więc zrobiłbym jedno i drugie. Zakładając, że wysuwasz się z obiektu Seam's EntityHome:

  1. W metodzie persist() uruchom zapytanie, aby upewnić się, że identyfikator logowania jest unikatowy. Jeśli nie jest dodawany komunikat o błędzie do odpowiedniej kontroli i zwraca wartość null.
  2. Wrap wezwanie do super.persist() i złapać ConstraintViolationException, wyświetlając komunikat o błędzie rodzajowe powielać

EDIT

Jak wspomniano Shervin tworząc JSF Validator jest świetny pomysł (zastąpienie) # 1 powyżej, ale powinieneś się spodziewać najgorszego i złapać wyjątek ConstraintViolationException.

+0

Jak zapobiec tym problemom, jeśli samodzielny klient wykonuje bezpośrednie wywołanie JPA. W jaki sposób możemy sprawić, że kod będzie ponownie wykorzystywany w tych scenariuszach? – Joe

+0

Czy samodzielny klient zawsze wykonuje bezpośrednie wywołanie JPA, czy może wymusić na nim wykonanie jakiegoś DAO? – mtpettyp

+0

Używamy komponentów Seam (EntityHome, EntityQuery interfaces), które obecnie służą jako klej pomiędzy interfejsem użytkownika a warstwą trwałości JPA. Nie jest dla mnie jasne, czy mógłbym wymusić na osobach niezależnych używanie warstwy abstrakcji szwu do utrwalania zamiast warstwy JPA bezpośrednio. Jeśli warstwa Seam musi być obecna, biblioteki Seam muszą zostać wysłane jako część zestawu narzędzi klienta. Nie jestem pewien, jaka jest najlepsza metoda tutaj. – Joe

2

Moja rada jest taka, że ​​jeśli możesz sprawdzić warunek, to sprawdź to np. W twoim przypadku wywołanie metody UserExists. Zgłaszanie wyjątków jest drogie i jest przeznaczone do wyjątkowych przypadków zwykle związanych z sytuacjami poza kontrolą użytkownika, np. dostęp do dysku itp.

Generalnie wykonujesz tę kontrolę w logice biznesowej przed wywołaniem dodania jednostki do bazy danych.

+1

Tak, moim jedynym zmartwieniem w tym podejściu jest to, że kilka zapytań musi być zwolnionych z prawie każdego stworzenia lub modyfikacji rekordu. Po prostu szukam najlepszego podejścia, które preferują użytkownicy. Dzięki – Joe

+1

Pytanie brzmi: co jest droższe, testowanie KAŻDEJ operacji zapisu do bazy danych lub obsługiwanie wyjątku tylko wtedy, gdy coś pójdzie nie tak? – Randy

6

Nie zgadzam się z obsługą wyjątku ConstraintException. Napisałem walidator, który sprawdza duplikaty przed zapisaniem i działa świetnie.

Oto przykład sprawdzania duplikatów wiadomości e-mail.

@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"))); 
     } 
    } 
} 
} 

A w swojej formie (xhtml) piszesz:

<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> 

W ten sposób będzie on zawsze sprawdzić poprawność pole przed zapisaniem. Możesz nawet umieścić tag: support, aby sprawdzać poprawność po zmianie fokusa.

+0

Jeśli twoja baza danych obsługuje unikatowe, wtedy rzuci wyjątek, a twój ogólny wyjątek zostanie naprawiony. –

+1

Nadal istnieje szansa, że ​​Twój DB może zostać zaktualizowany duplikatem wiadomości e-mail pomiędzy fazami JSF "Process Validations" i "Update Model" i zostanie zgłoszony wyjątek ConstraintException. – mtpettyp

+0

Nie jest prawdopodobne, aby tak się stało, jeśli korzystasz z Optymistycznego blokowania, tzn. '@ Wersja' –

Powiązane problemy