2010-01-30 4 views
11

Что является лучшим механизмом для предотвращения проверок нарушения ограничений перед созданием | модификация объекта?проверяет нарушение ограничений перед сохранением объекта

Предположим, что сущность «Пользователь» имеет значение «loginid» как уникальное ограничение, было бы разумно проверить, есть ли пользовательская запись уже с этим именем входа перед созданием или модификацией.

ИЛИ

вы позволите базу данных бросить ConstraintViolationException и обрабатывать это сообщение надлежащим образом в слое пользовательского интерфейса. Где такие проверки должны соблюдаться в рамках швов jboss.

Примечание: В настоящее время такие проверки не применяются к коду шва.

В настоящее время мы используем Seam 2.2, Richfaces with Hibernate.

ответ

6

Даже если вы проверяете условие в своем коде перед сохранением объекта пользователя, всегда существует вероятность того, что кто-то создаст дубликат идентификатора между проверкой времени и сохранением нового пользователя.

Однако будет легче отобразить соответствующее сообщение об ошибке в пользовательском интерфейсе, если вы выполните явную проверку. Если у вас есть несколько указаний на таблицу, то захват ConstraintViolationException не позволит вам легко определить, какое ограничение было нарушено.

Так что я бы сделал то и другое. Предполагая, что вы расширяетесь от EntityHome Seam:

  1. В методе persist() запустите запрос, чтобы убедиться, что идентификатор входа уникален. Если он не добавляет сообщение об ошибке в соответствующий элемент управления и возвращает значение null.
  2. Оберните вызов super.persist() и поймать ConstraintViolationException, выдает сообщение об ошибке общего дубликатом

EDIT

Как Shervin упоминается создание JSF валидатор отличная идея (замена) # 1 выше, но вы все равно должны ожидать худшего и поймать ConstraintViolationException.

+0

Как предотвратить эти проблемы, если отдельный клиент делает прямой вызов JPA. Как мы можем сделать код повторно используемым в этих сценариях? – Joe

+0

Является ли автономный клиент всегда прямым вызовом JPA или вы можете заставить его пройти через DAO? – mtpettyp

+0

Мы используем компоненты Seam (интерфейсы EntityHome, EntityQuery), которые в настоящее время служат как клей между пользовательским интерфейсом и уровнем сохранения JPA. Мне непонятно, могу ли я принуждать отдельных клиентов использовать слой абстракции Seam для сохранения, а не непосредственно для JPA-слоя. Если слой Seam должен присутствовать, библиотеки Seam должны быть отправлены как часть клиентского инструментария. Не уверен, что здесь лучше всего подходит – Joe

2

Мой совет заключается в том, что если вы можете проверить состояние, то проверьте его, например, в вашем случае вызов метода UserExists. Выбрасывание исключений является дорогостоящим и предназначено для исключительных случаев, обычно связанных с вещами, выходящими за ваше управление, например. доступ к диску и т. д.

Обычно вы выполняете эту проверку в своей бизнес-логике, прежде чем вызывать сущность, которая будет добавлена ​​в базу данных.

+1

Да, мое единственное беспокойство в этом подходе состоит в том, что несколько запросов нужно уволить практически для любого создания или изменения записи. Я просто ищу лучший подход, который предпочитают пользователи. Спасибо – Joe

+1

Вопрос в том, что дороже, тестирование на КАЖДУЮ запись в БД или обработку исключения только тогда, когда что-то идет не так? – Randy

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

Если ваша база данных обрабатывает уникальную, то она будет генерировать исключение, и ваш общий обработчик исключений исправит ее. –

+1

По-прежнему существует вероятность того, что ваша БД может быть обновлена ​​дублирующимся адресом электронной почты между этапами JSF «Проверка процесса» и «Обновить модель» и исключение ConstraintException. – mtpettyp

+0

Скорее всего, это произойдет, если вы используете оптимистичную блокировку, то есть '@ Version' –