This page is obsolete and is not maintained anymore.
Here are all the pieces you need to write a ConstraintValidator which has access to the Hibernate Session respectively Hibernate EntityManager. This example will implement a @Unique constraint as discussed in HV-230. The reason @Unique is not part of the built-in constraints is the fact that accessing the Session/EntityManager during a valiation is opening yourself up for potenital phantom reads. Think twice before you go for the following approach.
First you will need a custom ConstraintValidatorFactory. This factory has a handle to the Hibernate SessionFactory in order to inject it when a new ConstraintValidator is created.
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorFactory;
import org.hibernate.SessionFactory;
import org.hibernate.validator.constraints.SessionAwareConstraintValidator;
public class SessionAwareConstraintValidatorFactory extends ConstraintValidatorFactoryImpl
implements ConstraintValidatorFactory {
private SessionFactory sessionFactory;
public SessionAwareConstraintValidatorFactory() {
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public > T getInstance(Class key) {
T constraintValidator = super.getInstance( key );
if ( constraintValidator instanceof SessionAwareConstraintValidator ) {
( ( SessionAwareConstraintValidator ) constraintValidator ).setSessionFactory( sessionFactory );
}
return constraintValidator;
}
}
Next we introduce a base class for session aware ConstraintValidators:
import javax.validation.ConstraintValidatorContext;
import javax.validation.ValidationException;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public abstract class SessionAwareConstraintValidator {
private SessionFactory sessionFactory;
boolean openedNewTransaction;
private Session tmpSession;
public SessionAwareConstraintValidator() {
}
public boolean isValid(T value, ConstraintValidatorContext context) {
openTmpSession();
boolean result = isValidInSession( value, context );
closeTmpSession();
return result;
}
public abstract boolean isValidInSession(T value, ConstraintValidatorContext context);
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session getTmpSession() {
return tmpSession;
}
private void openTmpSession() {
Session currentSession;
try {
currentSession = getSessionFactory().getCurrentSession();
}
catch ( HibernateException e ) {
throw new ValidationException( "Unable to determine current Hibernate session", e );
}
if ( !currentSession.getTransaction().isActive() ) {
currentSession.beginTransaction();
openedNewTransaction = true;
}
try {
tmpSession = getSessionFactory().openSession( currentSession.connection() );
}
catch ( HibernateException e ) {
throw new ValidationException( "Unable to open temporary session", e );
}
}
private void closeTmpSession() {
if ( openedNewTransaction ) {
try {
getSessionFactory().getCurrentSession().getTransaction().commit();
}
catch ( HibernateException e ) {
throw new ValidationException( "Unable to commit transaction for temporary session", e );
}
}
try {
tmpSession.close();
}
catch ( HibernateException e ) {
throw new ValidationException( "Unable to close temporary Hibernate session", e );
}
}
}
Let's now first look at the constraint we want to create:
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import org.hibernate.validator.constraints.impl.UniqueValidator;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = { UniqueValidator.class })
@Target({ TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Unique {
String message() default "{org.hibernate.validator.constraints.Unique.message}";
String[] properties();
Class[] groups() default { };
Class[] payload() default { };
}
The actual validator implementation looks now like this:
import java.io.Serializable; import java.util.List; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import org.hibernate.EntityMode; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.engine.SessionImplementor; import org.hibernate.metadata.ClassMetadata; import org.hibernate.validator.constraints.SessionAwareConstraintValidator; import org.hibernate.validator.constraints.Unique; public class UniqueValidator extends SessionAwareConstraintValidator
Comments