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.
¿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
¿El cliente independiente siempre hace una llamada JPA directa o puede obligarlo a pasar por un DAO de algún tipo? – mtpettyp
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