2010-01-30 16 views
11

¿Cuál es el mejor mecanismo para evitar las verificaciones de violación de restricciones antes de la creación? modificación de una entidad?comprueba la violación de la restricción antes de persistir una entidad

Supongamos que si la entidad 'Usuario' tiene 'loginid' como la restricción única, sería conveniente comprobar si hay una entrada de usuario que ya tenga este nombre de inicio de sesión antes de la creación o modificación.

O

dejarías que la base de datos de lanzar un ConstraintViolationException y manejar apropiadamente este mensaje en la capa de interfaz de usuario. ¿Dónde deberían aplicarse tales controles en el marco de la jboss seam?

Nota: Actualmente no se aplican tales controles en el código seam-gen.

Actualmente utilizamos Seam 2.2, Richfaces con Hibernate.

Respuesta

6

Incluso si comprueba la condición en su código antes de persistir en el objeto del usuario, siempre existe la posibilidad de que alguien cree un ID de usuario duplicado entre el momento en que marca y el nuevo usuario.

Sin embargo, será más fácil mostrar un mensaje de error apropiado en la interfaz de usuario si realiza una comprobación explícita. Si tiene varias restricciones en la tabla, la captura de ConstraintViolationException no le permitirá determinar fácilmente qué restricción se ha violado.

Así que haría ambas cosas. Suponiendo que se extiende desde Seam's EntityHome:

  1. En el método persist() ejecute una consulta para asegurarse de que el ID de inicio de sesión sea único. Si no es, agregue un mensaje de error al control apropiado y devuelva nulo.
  2. envolver la llamada a super.persist() y coger el ConstraintViolationException, mostrando un mensaje de error genérico duplicado

EDITAR

Como Shervin menciona la creación de un validador de JSF es una gran idea (en sustitución) # 1 arriba, pero aún debes esperar lo peor y atrapar ConstraintViolationException.

+0

¿Cómo previene estos problemas si un cliente independiente realiza una llamada JPA directa? ¿Cómo podemos hacer que el código sea reutilizable en todos estos escenarios? – Joe

+0

¿El cliente independiente siempre hace una llamada JPA directa o puede obligarlo a pasar por un DAO de algún tipo? – mtpettyp

+0

Estamos utilizando componentes Seam (EntityHome, interfaces EntityQuery) que actualmente sirven como pegamento entre la interfaz de usuario y la capa de persistencia JPA. No me queda claro si podría hacer que los clientes independientes utilicen la capa de abstracción Seam para la persistencia en lugar de la capa JPA directamente. Si la capa Seam tiene que estar presente, las librerías Seam deben enviarse como parte del kit de herramientas del cliente. No estoy seguro de cuál es el mejor enfoque aquí – Joe

2

Mi consejo es que si puede verificar una condición, verifíquela, es decir, en su caso una llamada al método UserExists. Lanzar excepciones es costoso y está destinado a casos excepcionales que normalmente están relacionados con cosas fuera de su control, p. acceso a disco, etc.

En general, debe realizar esta verificación en la lógica de su negocio antes de llamar para que la entidad se agregue a la base de datos.

+1

Sí, mi única preocupación con este enfoque es que se deben realizar pocas consultas para casi cualquier creación o modificación del registro. Solo estoy buscando el mejor enfoque que los usuarios prefieran. Gracias – Joe

+1

La pregunta es: ¿qué es más caro, probando CADA escritura en el DB o manejando una excepción solo cuando algo sale mal? – Randy

6

No estoy de acuerdo con el manejo de ConstraintException. He escrito un validador que comprueba los duplicados antes de guardar, y funciona muy bien.

Aquí hay un ejemplo que verifica correos electrónicos duplicados.

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

Y en su forma (XHTML) se escribe:

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

De esta manera siempre habrá validar el campo antes de guardar. Incluso puede poner una etiqueta a: support para validar cuando se cambia el foco.

+0

Si su base de datos se maneja de manera única, arrojará una excepción y su controlador de excepción general la arreglará. –

+1

Todavía existe la posibilidad de que su base de datos se pueda actualizar con un correo electrónico duplicado entre las fases de JSF "Validación de proceso" y "Actualizar modelo" y se lanzaría una excepción de restricción. – mtpettyp

+0

No es probable que esto suceda si está utilizando el bloqueo optimista, es decir, '@ Version –

Cuestiones relacionadas