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