@UniqueConstraint Annotation does not validate the value of entity field. It will not catch the org.hibernate.exception.ConstraintViolationException.
Example:
@Table(name = "user", uniqueConstraints = @UniqueConstraint(columnNames = "username"))
Error:
ERROR [AbstractFlushingEventListener] Could not synchronize database state with session org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update...
SOLUTION: Custom Validator
----------------------------------------
To solve this problem, I created a custom validator to do the validation in JSF before persist the entity.
Seam provides annotations to allow us to use Seam components as JSF converters and validators.
@Name("usernameValidator")
@BypassInterceptors
@org.jboss.seam.annotations.faces.Validator
public class UsernameValidator implements javax.faces.validator.Validator {
public static final String VERIFICATION_MSG_EXPR = "#{messages['validator.duplicateError']}";
public void validate(FacesContext context, UIComponent cmp, Object value)throws ValidatorException
{
String ejbql = "select u from User u where u.username=:username";
String username = (String)value;
EntityManager em = (EntityManager) Component.getInstance("entityManager");
Query query = em.createQuery(ejbql);
query.setParameter("username", username);
List list = query.getResultList();
if (!list.isEmpty()) {
FacesMessage msg = new FacesMessage();
msg.setSummary(Interpolator.instance().interpolate(VERIFICATION_MSG_EXPR));
msg.setSeverity(FacesMessage.SEVERITY_WARN);
throw new ValidatorException(msg);
}
}
}
In JSF:
Registers the Seam component as a JSF validator. Below example is a validator which injects another Seam component. The injected component is used to validate the value.
<h:inputText id="username" value="#{userHome.instance.username}" validator="usernameValidator"/>
放棄者不會勝利,勝利者永不放棄。 ....................................................
ReplyDeletethis works fine but suppose if i am editing this is not working ie while editing i am forced to change username otherwise its still showing validator.duplicateError , is their any work around for this while editing
ReplyDeletehi.... below is the work around i m using. hope can help u..
ReplyDeletein UserHome.java add this method
public void handleUsernameChange(ValueChangeEvent e) {
try{
String newUsername = (String) e.getNewValue();
String savedUsername = getInstance().getUsername();
if(!newUsername.equals(savedUsername))
{
String ejbql = "select u from User u where lower(u.username)=:Username";
Query query = getEntityManager().createQuery(ejbql);
query.setParameter("Username", newUsername.toLowerCase());
List list = query.getResultList();
if (!list.isEmpty()) { facesMessages.addToControl("username2", FacesMessage.SEVERITY_ERROR, "#{messages['validator.duplicateError']}");
}
}
}catch(Exception ex){
System.err.println("exception: "+ex);
}
}
in JSF
s:fragment rendered="#{!userHome.managed}"
s:decorate id="usernameField" template="/layout/edit.xhtml"
ui:define name="label">Username
h:inputText id="username" size="25" maxlength="25" value="#{userHome.instance.username}" required="true"
validator="usernameValidator">
a:support event="onblur" reRender="usernameField" bypassUpdates="true" ajaxSingle="true"/>
h:inputText>
s:decorate>
/s:fragment>
s:fragment rendered="#{userHome.managed}">
s:decorate id="usernameField2" template="/layout/edit.xhtml">
ui:define name="label">Username
h:inputText id="username2" size="25" maxlength="25" value="#userHome.instance.username}" required="true" valueChangeListener="#{userHome.handleUsernameChange}">
a:support event="onblur" reRender="usernameField2" bypassUpdates="true" ajaxSingle="true"/>
/h:inputText>
/s:decorate>
/s:fragment>
There's a better solution for "edit" pages:
ReplyDeleteYou should modify sql as :
select u from User u where lower(u.username)=:Username and id <> u.id