[hibernate-commits] Hibernate SVN: r16516 - in core/trunk/annotations/src: main/java/org/hibernate/cfg/beanvalidation and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed May 6 11:52:27 EDT 2009


Author: epbernard
Date: 2009-05-06 11:52:26 -0400 (Wed, 06 May 2009)
New Revision: 16516

Added:
   core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java
Modified:
   core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java
Log:
ANN-828 Use Bean Validation for DDL generation

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java	2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -396,8 +396,13 @@
 				}
 			}
 		}
+		applyDDLOnBeanValidation( (Collection<PersistentClass>) classes.values(), getProperties() );
 	}
 
+	private void applyDDLOnBeanValidation(Collection<PersistentClass> persistentClasses, Properties properties) {
+		BeanValidationActivator.applyDDL( persistentClasses, properties );
+	}
+
 	/**
 	 * Processes FKSecondPass instances trying to resolve any
 	 * graph circularity (ie PK made of a many to one linking to

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java	2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationActivator.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -2,6 +2,9 @@
 
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collection;
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
 
@@ -11,6 +14,8 @@
 import org.hibernate.util.ReflectHelper;
 import org.hibernate.HibernateException;
 import org.hibernate.AssertionFailure;
+import org.hibernate.cfg.Environment;
+import org.hibernate.mapping.PersistentClass;
 import org.hibernate.event.EventListeners;
 
 /**
@@ -22,34 +27,37 @@
 
 	private static final String BV_DISCOVERY_CLASS = "javax.validation.Validation";
 	private static final String TYPE_SAFE_ACTIVATOR_CLASS = "org.hibernate.cfg.beanvalidation.TypeSafeActivator";
+	private static final String TYPE_SAFE_DDL_METHOD = "applyDDL";
 	private static final String TYPE_SAFE_ACTIVATOR_METHOD = "activateBeanValidation";
 	private static final String MODE_PROPERTY = "javax.persistence.validation.mode";
 
 	public static void activateBeanValidation(EventListeners eventListeners, Properties properties) {
-		ValidationMode mode = ValidationMode.getMode( properties.get( MODE_PROPERTY ) );
-		if (mode == ValidationMode.NONE) return;
+		Set<ValidationMode> modes = ValidationMode.getModes( properties.get( MODE_PROPERTY ) );
+		if ( modes.contains( ValidationMode.NONE ) ) return;
+		//desactivate not-null tracking at the core level when Bean Validation is on unless the user really ask for it
+		if ( properties.getProperty( Environment.CHECK_NULLABILITY ) == null ) {
+			properties.setProperty( Environment.CHECK_NULLABILITY, "false" );
+		}
+
 		try {
 			//load Validation
 			ReflectHelper.classForName( BV_DISCOVERY_CLASS, BeanValidationActivator.class );
 		}
 		catch ( ClassNotFoundException e ) {
 
-			if (mode == ValidationMode.CALLBACK) {
+			if ( modes.contains( ValidationMode.CALLBACK ) ) {
 				throw new HibernateException( "Bean Validation not available in the class path but required in " + MODE_PROPERTY );
 			}
-			else if (mode == ValidationMode.AUTO) {
+			else if (modes.contains( ValidationMode.AUTO ) ) {
 				//nothing to activate
 				return;
 			}
-			else {
-				throw new AssertionFailure( "Unexpected ValidationMode: " + mode );
-			}
 		}
 		try {
 			Class<?> activator = ReflectHelper.classForName( TYPE_SAFE_ACTIVATOR_CLASS, BeanValidationActivator.class );
-			Method buildDefaultValidatorFactory =
+			Method activateBeanValidation =
 					activator.getMethod( TYPE_SAFE_ACTIVATOR_METHOD, EventListeners.class, Properties.class );
-			buildDefaultValidatorFactory.invoke( null, eventListeners, properties );
+			activateBeanValidation.invoke( null, eventListeners, properties );
 		}
 		catch ( NoSuchMethodException e ) {
 			throw new HibernateException( "Unable to get the default Bean Validation factory", e);
@@ -65,18 +73,76 @@
 		}
 	}
 
+	public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties) {
+		Set<ValidationMode> modes = ValidationMode.getModes( properties.get( MODE_PROPERTY ) );
+		if ( ! ( modes.contains( ValidationMode.DDL ) || modes.contains( ValidationMode.AUTO ) ) ) return;
+		try {
+			//load Validation
+			ReflectHelper.classForName( BV_DISCOVERY_CLASS, BeanValidationActivator.class );
+		}
+		catch ( ClassNotFoundException e ) {
+			if ( modes.contains( ValidationMode.DDL ) ) {
+				throw new HibernateException( "Bean Validation not available in the class path but required in " + MODE_PROPERTY );
+			}
+			else if (modes.contains( ValidationMode.AUTO ) ) {
+				//nothing to activate
+				return;
+			}
+		}
+		try {
+			Class<?> activator = ReflectHelper.classForName( TYPE_SAFE_ACTIVATOR_CLASS, BeanValidationActivator.class );
+			Method applyDDL =
+					activator.getMethod( TYPE_SAFE_DDL_METHOD, Collection.class, Properties.class );
+			applyDDL.invoke( null, persistentClasses, properties );
+		}
+		catch ( NoSuchMethodException e ) {
+			throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+		}
+		catch ( IllegalAccessException e ) {
+			throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+		}
+		catch ( InvocationTargetException e ) {
+			throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+		}
+		catch ( ClassNotFoundException e ) {
+			throw new HibernateException( "Unable to get the default Bean Validation factory", e);
+		}
+	}
+
 	private static enum ValidationMode {
 		AUTO,
 		CALLBACK,
-		NONE;
+		NONE,
+		DDL;
 
-		public static ValidationMode getMode(Object modeProperty) {
+		public static Set<ValidationMode> getModes(Object modeProperty) {
+			Set<ValidationMode> modes = new HashSet<ValidationMode>(3);
 			if (modeProperty == null) {
+				modes.add(ValidationMode.AUTO);
+			}
+			else {
+				final String[] modesInString = modeProperty.toString().split( "," );
+				for ( String modeInString : modesInString ) {
+					modes.add( getMode(modeInString) );
+				}
+			}
+			if ( modes.size() > 1 && ( modes.contains( ValidationMode.AUTO ) || modes.contains( ValidationMode.NONE ) ) ) {
+				StringBuilder message = new StringBuilder( "Incompatible validation modes mixed: " );
+				for (ValidationMode mode : modes) {
+					message.append( mode ).append( ", " );
+				}
+				throw new HibernateException( message.substring( 0, message.length() - 2 ) );
+			}
+			return modes;
+		}
+
+		private static ValidationMode getMode(String modeProperty) {
+			if (modeProperty == null || modeProperty.length() == 0) {
 				return AUTO;
 			}
 			else {
 				try {
-					return valueOf( modeProperty.toString().toUpperCase() );
+					return valueOf( modeProperty.trim().toUpperCase() );
 				}
 				catch ( IllegalArgumentException e ) {
 					throw new HibernateException( "Unknown validation mode in " + MODE_PROPERTY + ": " + modeProperty.toString() );

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java	2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -6,6 +6,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
 import javax.validation.ValidatorFactory;
 import javax.validation.ConstraintViolation;
 import javax.validation.TraversableResolver;
@@ -21,6 +22,8 @@
 import org.hibernate.event.PreDeleteEvent;
 import org.hibernate.EntityMode;
 import org.hibernate.HibernateException;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.persister.entity.EntityPersister;
 import org.hibernate.util.ReflectHelper;
 
 /**
@@ -29,83 +32,45 @@
 //FIXME review exception model
 public class BeanValidationEventListener implements
 		PreInsertEventListener, PreUpdateEventListener, PreDeleteEventListener {
-	private static final String JPA_GROUP_PREFIX = "javax.persistence.validation.group.";
-	private static final Class<?>[] DEFAULT_GROUPS = new Class<?>[] { Default.class };
-	private static final Class<?>[] EMPTY_GROUPS = new Class<?>[] { };
 
+
 	private ValidatorFactory factory;
-	private TraversableResolver tr;
-	private Map<Operation, Class<?>[]> groupsPerOperation = new HashMap<Operation, Class<?>[]>(3);
+	private ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister =
+			new ConcurrentHashMap<EntityPersister, Set<String>>();
+	private GroupsPerOperation groupsPerOperation;
 
 
 	public BeanValidationEventListener(ValidatorFactory factory, Properties properties) {
 		this.factory = factory;
-		setGroupsForOperation( Operation.INSERT, properties );
-		setGroupsForOperation( Operation.UPDATE, properties );
-		setGroupsForOperation( Operation.DELETE, properties );
+		groupsPerOperation = new GroupsPerOperation(properties);
 	}
 
-	private void setGroupsForOperation(Operation operation, Properties properties) {
-		Object property = properties.get( JPA_GROUP_PREFIX + operation.getGroupPropertyName() );
 
-		Class<?>[] groups;
-		if ( property == null ) {
-			groups = operation == Operation.DELETE ? EMPTY_GROUPS : DEFAULT_GROUPS;
-		}
-		else {
-			if ( property instanceof String ) {
-				String stringProperty = (String) property;
-				String[] groupNames = stringProperty.split( "," );
-				if ( groupNames.length == 1 && groupNames[0].equals( "" ) ) {
-					groups = EMPTY_GROUPS;
-				}
-				else {
-					List<Class<?>> groupsList = new ArrayList<Class<?>>(groupNames.length);
-					for (String groupName : groupNames) {
-						String cleanedGroupName = groupName.trim();
-						if ( cleanedGroupName.length() > 0) {
-							try {
-								groupsList.add( ReflectHelper.classForName( cleanedGroupName ) );
-							}
-							catch ( ClassNotFoundException e ) {
-								throw new HibernateException( "Unable to load class " + cleanedGroupName, e );
-							}
-						}
 
-					}
-					groups = groupsList.toArray( new Class<?>[groupsList.size()] );
-				}
-			}
-			else if ( property instanceof Class<?>[] ) {
-				groups = (Class<?>[]) property;
-			}
-			else {
-				//null is bad and excluded by instanceof => exception is raised
-				throw new HibernateException( JPA_GROUP_PREFIX + operation.getGroupPropertyName() + " is of unknown type: String or Class<?>[] only");
-			}
-		}
-		groupsPerOperation.put( operation, groups );
-	}
-
 	public boolean onPreInsert(PreInsertEvent event) {
-		validate( event.getEntity(), event.getSession().getEntityMode(), Operation.INSERT );
+		validate( event.getEntity(), event.getSession().getEntityMode(), event.getPersister(),
+				event.getSession().getFactory(), GroupsPerOperation.Operation.INSERT );
 		return false;
 	}
 
 	public boolean onPreUpdate(PreUpdateEvent event) {
-		validate( event.getEntity(), event.getSession().getEntityMode(), Operation.UPDATE );
+		validate( event.getEntity(), event.getSession().getEntityMode(), event.getPersister(),
+				event.getSession().getFactory(), GroupsPerOperation.Operation.UPDATE );
 		return false;
 	}
 
 	public boolean onPreDelete(PreDeleteEvent event) {
-		validate( event.getEntity(), event.getSession().getEntityMode(), Operation.DELETE );
+		validate( event.getEntity(), event.getSession().getEntityMode(), event.getPersister(),
+				event.getSession().getFactory(),  GroupsPerOperation.Operation.DELETE );
 		return false;
 	}
 
-	private <T> void validate(T object, EntityMode mode, Operation operation) {
+	private <T> void validate(T object, EntityMode mode, EntityPersister persister,
+							  SessionFactoryImplementor sessionFactory, GroupsPerOperation.Operation operation) {
 		if ( object == null || mode != EntityMode.POJO ) return;
+		TraversableResolver tr = new HibernateTraversableResolver( persister, associationsPerEntityPersister, sessionFactory );
 		Validator validator = factory.usingContext()
-										//.traversableResolver( tr )
+										.traversableResolver( tr )
 										.getValidator();
 		final Class<?>[] groups = groupsPerOperation.get( operation );
 		if ( groups.length > 0 ) {
@@ -117,7 +82,7 @@
 				//FIXME add Set<ConstraintViolation<?>>
 				throw new ConstraintViolationException(
 						"Invalid object at " + operation.getName() + " time for groups " + toString( groups ),
-						(Set<ConstraintViolation>) unsafeViolations);
+						(Set<ConstraintViolation<?>>) unsafeViolations);
 			}
 		}
 	}
@@ -131,26 +96,6 @@
 		return toString.toString();
 	}
 
-	private static enum Operation {
-		INSERT("persist", "pre-persist"),
-		UPDATE("update", "pre-update"),
-		DELETE("remove", "pre-remove");
 
-		private String exposedName;
-		private String groupPropertyName;
 
-		Operation(String exposedName, String groupProperty) {
-			this.exposedName = exposedName;
-			this.groupPropertyName = groupProperty;
-		}
-
-		public String getName() {
-			return exposedName;
-		}
-
-		public String getGroupPropertyName() {
-			return groupPropertyName;
-		}
-	}
-
 }

Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java	                        (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/GroupsPerOperation.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,101 @@
+package org.hibernate.cfg.beanvalidation;
+
+import java.util.Properties;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import javax.validation.groups.Default;
+
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.HibernateException;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class GroupsPerOperation {
+
+	private static final String JPA_GROUP_PREFIX = "javax.persistence.validation.group.";
+	private static final String HIBERNATE_GROUP_PREFIX = "org.hibernate.validator.group.";
+	private static final Class<?>[] DEFAULT_GROUPS = new Class<?>[] { Default.class };
+	private static final Class<?>[] EMPTY_GROUPS = new Class<?>[] { };
+
+	private Map<Operation, Class<?>[]> groupsPerOperation = new HashMap<Operation, Class<?>[]>(4);
+
+	public GroupsPerOperation(Properties properties) {
+		setGroupsForOperation( Operation.INSERT, properties );
+		setGroupsForOperation( Operation.UPDATE, properties );
+		setGroupsForOperation( Operation.DELETE, properties );
+		setGroupsForOperation( Operation.DDL, properties );
+	}
+
+	private void setGroupsForOperation(Operation operation, Properties properties) {
+		Object property = properties.get( operation.getGroupPropertyName() );
+
+		Class<?>[] groups;
+		if ( property == null ) {
+			groups = operation == Operation.DELETE ? EMPTY_GROUPS : DEFAULT_GROUPS;
+		}
+		else {
+			if ( property instanceof String ) {
+				String stringProperty = (String) property;
+				String[] groupNames = stringProperty.split( "," );
+				if ( groupNames.length == 1 && groupNames[0].equals( "" ) ) {
+					groups = EMPTY_GROUPS;
+				}
+				else {
+					List<Class<?>> groupsList = new ArrayList<Class<?>>(groupNames.length);
+					for (String groupName : groupNames) {
+						String cleanedGroupName = groupName.trim();
+						if ( cleanedGroupName.length() > 0) {
+							try {
+								groupsList.add( ReflectHelper.classForName( cleanedGroupName ) );
+							}
+							catch ( ClassNotFoundException e ) {
+								throw new HibernateException( "Unable to load class " + cleanedGroupName, e );
+							}
+						}
+					}
+					groups = groupsList.toArray( new Class<?>[groupsList.size()] );
+				}
+			}
+			else if ( property instanceof Class<?>[] ) {
+				groups = (Class<?>[]) property;
+			}
+			else {
+				//null is bad and excluded by instanceof => exception is raised
+				throw new HibernateException( JPA_GROUP_PREFIX + operation.getGroupPropertyName() + " is of unknown type: String or Class<?>[] only");
+			}
+		}
+		groupsPerOperation.put( operation, groups );
+	}
+
+	public Class<?>[] get(Operation operation) {
+		return groupsPerOperation.get( operation );
+	}
+
+	public static enum Operation {
+		INSERT("persist", JPA_GROUP_PREFIX + "pre-persist"),
+		UPDATE("update", JPA_GROUP_PREFIX + "pre-update"),
+		DELETE("remove", JPA_GROUP_PREFIX + "pre-remove"),
+		DDL("ddl", HIBERNATE_GROUP_PREFIX + "ddl");
+
+
+		private String exposedName;
+		private String groupPropertyName;
+
+		Operation(String exposedName, String groupProperty) {
+			this.exposedName = exposedName;
+			this.groupPropertyName = groupProperty;
+		}
+
+		public String getName() {
+			return exposedName;
+		}
+
+		public String getGroupPropertyName() {
+			return groupPropertyName;
+		}
+	}
+
+}

Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java	                        (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/HibernateTraversableResolver.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,92 @@
+package org.hibernate.cfg.beanvalidation;
+
+import java.lang.annotation.ElementType;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.validation.TraversableResolver;
+
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.Hibernate;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.type.ComponentType;
+import org.hibernate.type.CollectionType;
+import org.hibernate.type.AbstractComponentType;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class HibernateTraversableResolver implements TraversableResolver {
+	private Set<String> associations;
+
+	public HibernateTraversableResolver(
+			EntityPersister persister,
+			ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister, 
+			SessionFactoryImplementor factory) {
+		this.associations = associationsPerEntityPersister.get( persister );
+		if (this.associations == null) {
+			this.associations = new HashSet<String>();
+			addAssociationsToTheSetForAllProperties( persister.getPropertyNames(), persister.getPropertyTypes(), "", factory );
+			associationsPerEntityPersister.put( persister, associations );
+		}
+	}
+
+	private void addAssociationsToTheSetForAllProperties(String[] names, Type[] types, String prefix, SessionFactoryImplementor factory) {
+		final int length = names.length;
+		for( int index = 0 ; index < length; index++ ) {
+			addAssociationsToTheSetForOneProperty( names[index], types[index], prefix, factory );
+		}
+	}
+
+	private void addAssociationsToTheSetForOneProperty(String name, Type type, String prefix, SessionFactoryImplementor factory) {
+
+		if ( type.isCollectionType() ) {
+			CollectionType collType = (CollectionType) type;
+			Type assocType = collType.getElementType( factory );
+			addAssociationsToTheSetForOneProperty(name, assocType, prefix, factory);
+		}
+		//ToOne association
+		else if ( type.isEntityType() || type.isAnyType() ) {
+			associations.add( prefix + name );
+		} else if ( type.isComponentType() ) {
+			AbstractComponentType componentType = (AbstractComponentType) type;
+			addAssociationsToTheSetForAllProperties(
+					componentType.getPropertyNames(),
+					componentType.getSubtypes(),
+					(prefix.equals( "" ) ? name : prefix + name) + ".",
+					factory);
+		}
+	}
+
+	private String getCleanPathWoBraket(String traversableProperty, String pathToTraversableObject) {
+		String path = pathToTraversableObject.equals( "" ) ?
+				traversableProperty :
+				pathToTraversableObject + "." + traversableProperty;
+		String[] paths = path.split( "\\[.*\\]" );
+		path = "";
+		for (String subpath : paths) {
+			path += subpath;
+		}
+		return path;
+	}
+
+	public boolean isReachable(Object traversableObject,
+						  String traversableProperty,
+						  Class<?> rootBeanType,
+						  String pathToTraversableObject,
+						  ElementType elementType) {
+		//lazy, don't load
+		return Hibernate.isInitialized( traversableObject )
+				&& Hibernate.isPropertyInitialized( traversableObject, traversableProperty );
+	}
+
+	public boolean isCascadable(Object traversableObject,
+						  String traversableProperty,
+						  Class<?> rootBeanType,
+						  String pathToTraversableObject,
+						  ElementType elementType) {
+		String path = getCleanPathWoBraket( traversableProperty, pathToTraversableObject );
+		return ! associations.contains(path);
+	}
+}

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java	2009-05-06 15:20:18 UTC (rev 16515)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/TypeSafeActivator.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -1,22 +1,49 @@
 package org.hibernate.cfg.beanvalidation;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
-import java.util.Arrays;
 import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Collection;
+import javax.validation.BeanDescriptor;
+import javax.validation.ConstraintDescriptor;
+import javax.validation.PropertyDescriptor;
+import javax.validation.Validation;
 import javax.validation.ValidatorFactory;
-import javax.validation.Validation;
+import javax.validation.constraints.Digits;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.hibernate.AssertionFailure;
 import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
 import org.hibernate.event.EventListeners;
+import org.hibernate.event.PreDeleteEventListener;
 import org.hibernate.event.PreInsertEventListener;
 import org.hibernate.event.PreUpdateEventListener;
-import org.hibernate.event.PreDeleteEventListener;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.SingleTableSubclass;
+import org.hibernate.util.ReflectHelper;
 
 /**
  * @author Emmanuel Bernard
  */
 class TypeSafeActivator {
 
+	private static final Logger logger = LoggerFactory.getLogger( TypeSafeActivator.class );
+
 	private static final String FACTORY_PROPERTY = "javax.persistence.validation.factory";
 
 	public static void activateBeanValidation(EventListeners eventListeners, Properties properties) {
@@ -51,7 +78,209 @@
 		}
 	}
 
-	static ValidatorFactory getValidatorFactory(Map<Object, Object> properties) {
+	public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties) {
+		ValidatorFactory factory = getValidatorFactory( properties );
+		Class<?>[] groupsArray = new GroupsPerOperation( properties ).get( GroupsPerOperation.Operation.DDL );
+		Set<Class<?>> groups = new HashSet<Class<?>>( Arrays.asList( groupsArray ) );
+
+		for ( PersistentClass persistentClass : persistentClasses ) {
+			final String className = persistentClass.getClassName();
+
+			if ( className == null || className.length() == 0) continue;
+			Class<?> clazz;
+			try {
+				clazz = ReflectHelper.classForName( className, TypeSafeActivator.class );
+			}
+			catch ( ClassNotFoundException e ) {
+				throw new AssertionFailure( "Entity class not found", e);
+			}
+			
+			try {
+				applyDDL( "", persistentClass, clazz, factory, groups, true );
+			}
+			catch (Exception e) {
+				logger.warn( "Unable to apply constraints on DDL for " + className, e );
+			}
+		}
+	}
+
+	private static void applyDDL(String prefix,
+								 PersistentClass persistentClass,
+								 Class<?> clazz,
+								 ValidatorFactory factory,
+								 Set<Class<?>> groups,
+								 boolean activateNotNull) {
+		final BeanDescriptor descriptor = factory.getValidator().getConstraintsForClass( clazz );
+		//no bean level constraints can be applied, go to the properties
+
+		for ( PropertyDescriptor propertyDesc : descriptor.getConstrainedProperties() ) {
+			Property property = findPropertyByName( persistentClass, prefix + propertyDesc.getPropertyName() );
+			boolean hasNotNull = false;
+			if ( property != null ) {
+				hasNotNull = applyConstraints( propertyDesc.getConstraintDescriptors(), property, propertyDesc, groups, activateNotNull );
+				if ( property.isComposite() && propertyDesc.isCascaded() ) {
+					Class<?> componentClass = ( ( Component ) property.getValue() ).getComponentClass();
+
+					/*
+					 * we can apply not null if the upper component let's us activate not null
+					 * and if the property is not null.
+					 * Otherwise, all sub columns should be left nullable
+					 */
+					final boolean canSetNotNullOnColumns = activateNotNull && hasNotNull;
+					applyDDL( prefix + propertyDesc.getPropertyName() + ".",
+							persistentClass, componentClass, factory, groups,
+							canSetNotNullOnColumns
+					);
+				}
+				//FIXME add collection of components
+			}
+		}
+	}
+
+	private static boolean applyConstraints(Set<ConstraintDescriptor<?>> constraintDescriptors,
+										 Property property,
+										 PropertyDescriptor propertyDesc,
+										 Set<Class<?>> groups, boolean canApplyNotNull) {
+		boolean hasNotNull = false;
+		for (ConstraintDescriptor<?> descriptor : constraintDescriptors) {
+			if ( groups != null && Collections.disjoint( descriptor.getGroups(), groups) ) continue;
+
+			if ( canApplyNotNull ) {
+				hasNotNull = hasNotNull || applyNotNull( property, descriptor );
+			}
+			applyDigits( property, descriptor );
+			applySize( property, descriptor, propertyDesc );
+			applyMin( property, descriptor );
+			applyMax( property, descriptor );
+
+			//pass an empty set as composing constraints inherit the main constraint and thus are matching already
+			hasNotNull = hasNotNull || applyConstraints(
+					descriptor.getComposingConstraints(),
+					property, propertyDesc, null,
+					canApplyNotNull );
+		}
+		return hasNotNull;
+	}
+
+	private static void applyMin(Property property, ConstraintDescriptor<?> descriptor) {
+		if ( Min.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+			@SuppressWarnings( "unchecked" )
+			ConstraintDescriptor<Min> minConstraint = (ConstraintDescriptor<Min>) descriptor;
+			long min = minConstraint.getAnnotation().value();
+			Column col = (Column) property.getColumnIterator().next();
+			col.setCheckConstraint( col.getName() + ">=" + min );
+		}
+	}
+
+	private static void applyMax(Property property, ConstraintDescriptor<?> descriptor) {
+		if ( Max.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+			@SuppressWarnings( "unchecked" )
+			ConstraintDescriptor<Max> maxConstraint = (ConstraintDescriptor<Max>) descriptor;
+			long max = maxConstraint.getAnnotation().value();
+			Column col = (Column) property.getColumnIterator().next();
+			col.setCheckConstraint( col.getName() + "<=" + max );
+		}
+	}
+
+	private static boolean applyNotNull(Property property, ConstraintDescriptor<?> descriptor) {
+		boolean hasNotNull = false;
+		if ( NotNull.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+			if ( ! ( property.getPersistentClass() instanceof SingleTableSubclass ) ) {
+				//single table should not be forced to null
+				if ( !property.isComposite() ) { //composite should not add not-null on all columns
+					@SuppressWarnings( "unchecked" )
+					Iterator<Column> iter = (Iterator<Column>) property.getColumnIterator();
+					while ( iter.hasNext() ) {
+						iter.next().setNullable( false );
+						hasNotNull = true;
+					}
+				}
+			}
+			hasNotNull = true;
+		}
+		return hasNotNull;
+	}
+
+	private static void applyDigits(Property property, ConstraintDescriptor<?> descriptor) {
+		if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) {
+			@SuppressWarnings( "unchecked" )
+			ConstraintDescriptor<Digits> digitsConstraint = (ConstraintDescriptor<Digits>) descriptor;
+			int integerDigits = digitsConstraint.getAnnotation().integer();
+			int fractionalDigits = digitsConstraint.getAnnotation().fraction();
+			Column col = (Column) property.getColumnIterator().next();
+			col.setPrecision( integerDigits + fractionalDigits );
+			col.setScale( fractionalDigits );
+		}
+	}
+
+	private static void applySize(Property property, ConstraintDescriptor<?> descriptor, PropertyDescriptor propertyDesc) {
+		if ( Size.class.equals( descriptor.getAnnotation().annotationType() )
+				&& String.class.equals( propertyDesc.getType() ) ) {
+			@SuppressWarnings( "unchecked" )
+			ConstraintDescriptor<Size> sizeConstraint = (ConstraintDescriptor<Size>) descriptor;
+			int max = sizeConstraint.getAnnotation().max();
+			Column col = (Column) property.getColumnIterator().next();
+			if ( max < Integer.MAX_VALUE ) col.setLength( max );
+		}
+	}
+
+	/**
+	 * Retrieve the property by path in a recursive way, including IndetifierProperty in the loop
+	 * If propertyName is null or empty, the IdentifierProperty is returned
+	 */
+	private static Property findPropertyByName(PersistentClass associatedClass, String propertyName) {
+		Property property = null;
+		Property idProperty = associatedClass.getIdentifierProperty();
+		String idName = idProperty != null ? idProperty.getName() : null;
+		try {
+			if ( propertyName == null
+					|| propertyName.length() == 0
+					|| propertyName.equals( idName ) ) {
+				//default to id
+				property = idProperty;
+			}
+			else {
+				if ( propertyName.indexOf( idName + "." ) == 0 ) {
+					property = idProperty;
+					propertyName = propertyName.substring( idName.length() + 1 );
+				}
+				StringTokenizer st = new StringTokenizer( propertyName, ".", false );
+				while ( st.hasMoreElements() ) {
+					String element = (String) st.nextElement();
+					if ( property == null ) {
+						property = associatedClass.getProperty( element );
+					}
+					else {
+						if ( ! property.isComposite() ) return null;
+						property = ( ( Component ) property.getValue() ).getProperty( element );
+					}
+				}
+			}
+		}
+		catch ( MappingException e) {
+			try {
+				//if we do not find it try to check the identifier mapper
+				if ( associatedClass.getIdentifierMapper() == null ) return null;
+				StringTokenizer st = new StringTokenizer( propertyName, ".", false );
+				while ( st.hasMoreElements() ) {
+					String element = (String) st.nextElement();
+					if ( property == null ) {
+						property = associatedClass.getIdentifierMapper().getProperty( element );
+					}
+					else {
+						if ( ! property.isComposite() ) return null;
+						property = ( (Component) property.getValue() ).getProperty( element );
+					}
+				}
+			}
+			catch (MappingException ee) {
+				return null;
+			}
+		}
+		return property;
+	}
+
+	private static ValidatorFactory getValidatorFactory(Map<Object, Object> properties) {
 		ValidatorFactory factory = null;
 		if ( properties != null ) {
 			Object unsafeProperty = properties.get( FACTORY_PROPERTY );

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Address.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,103 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Transient;
+import javax.persistence.Id;
+import javax.validation.constraints.Size;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.AssertTrue;
+
+ at Entity
+public class Address {
+	@NotNull
+	public static String blacklistedZipCode;
+
+	private String line1;
+	private String line2;
+	private String zip;
+	private String state;
+	@Size(max = 20)
+	@NotNull
+	private String country;
+	private long id;
+	private boolean internalValid = true;
+	@Min(-2) @Max(value=50)
+	public int floor;
+
+	public String getCountry() {
+		return country;
+	}
+
+	public void setCountry(String country) {
+		this.country = country;
+	}
+
+	@NotNull
+	public String getLine1() {
+		return line1;
+	}
+
+	public void setLine1(String line1) {
+		this.line1 = line1;
+	}
+
+	public String getLine2() {
+		return line2;
+	}
+
+	public void setLine2(String line2) {
+		this.line2 = line2;
+	}
+
+	@Size(max = 3)
+	@NotNull
+	public String getState() {
+		return state;
+	}
+
+	public void setState(String state) {
+		this.state = state;
+	}
+
+	@Size(max = 5)
+	@Pattern(regexp = "[0-9]+")
+	@NotNull
+	public String getZip() {
+		return zip;
+	}
+
+	public void setZip(String zip) {
+		this.zip = zip;
+	}
+
+	@AssertTrue
+	@Transient
+	public boolean isValid() {
+		return true;
+	}
+
+	@AssertTrue
+	@Transient
+	private boolean isInternalValid() {
+		return internalValid;
+	}
+
+	public void setInternalValid(boolean internalValid) {
+		this.internalValid = internalValid;
+	}
+
+	@Id
+	@Min(1)
+	@Max(2000)
+	public long getId() {
+		return id;
+	}
+
+	public void setId(long id) {
+		this.id = id;
+	}
+
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Button.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,34 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Embeddable;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Max;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Embeddable
+public class Button {
+
+	private String name;
+
+	private Integer size;
+
+	@NotNull
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	@Max( 10 )
+	public Integer getSize() {
+		return size;
+	}
+
+	public void setSize(Integer size) {
+		this.size = size;
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Color.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,33 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class Color {
+	private Integer id;
+	private String name;
+
+	@Id @GeneratedValue
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	@NotNull
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DDLTest.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,60 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Property;
+import org.hibernate.test.annotations.TestCase;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class DDLTest extends TestCase {
+
+	public void testBasicDDL() {
+		PersistentClass classMapping = getCfg().getClassMapping( Address.class.getName() );
+		//new ClassValidator( Address.class, ResourceBundle.getBundle("messages", Locale.ENGLISH) ).apply( classMapping );
+		Column stateColumn = (Column) classMapping.getProperty( "state" ).getColumnIterator().next();
+		assertEquals( stateColumn.getLength(), 3 );
+		Column zipColumn = (Column) classMapping.getProperty( "zip" ).getColumnIterator().next();
+		assertEquals( zipColumn.getLength(), 5 );
+		assertFalse( zipColumn.isNullable() );
+	}
+
+	public void testApplyOnIdColumn() throws Exception {
+		PersistentClass classMapping = getCfg().getClassMapping( Tv.class.getName() );
+		Column serialColumn = (Column) classMapping.getIdentifierProperty().getColumnIterator().next();
+		assertEquals( "Vaidator annotation not applied on ids", 2, serialColumn.getLength() );
+	}
+
+	public void testApplyOnManyToOne() throws Exception {
+		PersistentClass classMapping = getCfg().getClassMapping( TvOwner.class.getName() );
+		Column serialColumn = (Column) classMapping.getProperty( "tv" ).getColumnIterator().next();
+		assertEquals( "Validator annotations not applied on associations", false, serialColumn.isNullable() );
+	}
+
+	public void testSingleTableAvoidNotNull() throws Exception {
+		PersistentClass classMapping = getCfg().getClassMapping( Rock.class.getName() );
+		Column serialColumn = (Column) classMapping.getProperty( "bit" ).getColumnIterator().next();
+		assertTrue( "Notnull should not be applised on single tables", serialColumn.isNullable() );
+	}
+
+	public void testNotNullOnlyAppliedIfEmbeddedIsNotNullItself() throws Exception {
+		PersistentClass classMapping = getCfg().getClassMapping( Tv.class.getName() );
+		Property property = classMapping.getProperty( "tuner.frequency" );
+		Column serialColumn = (Column) property.getColumnIterator().next();
+		assertEquals( "Validator annotations are applied on tunner as it is @NotNull", false, serialColumn.isNullable() );
+
+		property = classMapping.getProperty( "recorder.time" );
+		serialColumn = (Column) property.getColumnIterator().next();
+		assertEquals( "Validator annotations are applied on tunner as it is @NotNull", true, serialColumn.isNullable() );
+	}
+
+	protected Class<?>[] getMappings() {
+		return new Class<?>[] {
+				Address.class,
+				Tv.class,
+				TvOwner.class,
+				Rock.class
+		};
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Display.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,35 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class Display {
+	private Integer id;
+
+	private String brand;
+
+	@Id
+	@GeneratedValue
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	@NotNull
+	public String getBrand() {
+		return brand;
+	}
+
+	public void setBrand(String brand) {
+		this.brand = brand;
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/DisplayConnector.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,38 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Embeddable;
+import javax.persistence.OneToOne;
+import javax.persistence.CascadeType;
+import javax.persistence.ManyToOne;
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Embeddable
+public class DisplayConnector {
+
+	private int number;
+	private Display display;
+
+	@Min(1)
+	public int getNumber() {
+		return number;
+	}
+
+	public void setNumber(int number) {
+		this.number = number;
+	}
+
+	@ManyToOne(cascade = CascadeType.PERSIST)
+	@Valid
+	public Display getDisplay() {
+		return display;
+	}
+
+	public void setDisplay(Display display) {
+		this.display = display;
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/HibernateTraversableResolverTest.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,191 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.math.BigDecimal;
+import javax.validation.ConstraintViolationException;
+import javax.validation.ConstraintViolation;
+
+import org.hibernate.test.annotations.TestCase;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class HibernateTraversableResolverTest extends TestCase {
+	public void testNonLazyAssocFieldWithConstraintsFailureExpected() {
+		Session s = openSession(  );
+		Transaction tx = s.beginTransaction();
+
+		Screen screen = new Screen();
+		screen.setPowerSupply( null );
+		try {
+			s.persist( screen );
+			s.flush();
+			fail("@NotNull on a non lazy association is not evaluated");
+		}
+		catch ( ConstraintViolationException e ) {
+			assertEquals( 1, e.getConstraintViolations().size() );
+		}
+
+		tx.rollback();
+		s.close();
+	}
+
+	public void testEmbedded() {
+		Session s = openSession(  );
+		Transaction tx = s.beginTransaction();
+
+		Screen screen = new Screen();
+		PowerSupply ps = new PowerSupply();
+		screen.setPowerSupply( ps );
+		Button button = new Button();
+		button.setName( null );
+		button.setSize( 3 );
+		screen.setStopButton( button );
+		try {
+			s.persist( screen );
+			s.flush();
+			fail("@NotNull on empedded property is not evaluated");
+		}
+		catch ( ConstraintViolationException e ) {
+			assertEquals( 1, e.getConstraintViolations().size() );
+			ConstraintViolation<?> cv = e.getConstraintViolations().iterator().next();
+			assertEquals( Screen.class, cv.getRootBeanClass() );
+			assertEquals( "stopButton.name", cv.getPropertyPath() );
+		}
+
+		tx.rollback();
+		s.close();
+	}
+
+	public void testToOneAssocNotValidated() {
+		Session s = openSession(  );
+		Transaction tx = s.beginTransaction();
+
+		Screen screen = new Screen();
+		PowerSupply ps = new PowerSupply();
+		ps.setPosition( "1" );
+		ps.setPower( new BigDecimal( 350 ) );
+		screen.setPowerSupply( ps );
+		try {
+			s.persist( screen );
+			s.flush();
+			fail("Associated objects should not be validated");
+		}
+		catch ( ConstraintViolationException e ) {
+			assertEquals( 1, e.getConstraintViolations().size() );
+			final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+			assertEquals( PowerSupply.class, constraintViolation.getRootBeanClass() );
+		}
+
+		tx.rollback();
+		s.close();
+	}
+
+	public void testCollectionAssocNotValidated() {
+		Session s = openSession(  );
+		Transaction tx = s.beginTransaction();
+
+		Screen screen = new Screen();
+		screen.setStopButton( new Button() );
+		screen.getStopButton().setName( "STOOOOOP" );
+		PowerSupply ps = new PowerSupply();
+		screen.setPowerSupply( ps );
+		Color c = new Color();
+		c.setName( "Blue" );
+		s.persist( c );
+		c.setName( null );
+		screen.getDisplayColors().add( c );
+		try {
+			s.persist( screen );
+			s.flush();
+			fail("Associated objects should not be validated");
+		}
+		catch ( ConstraintViolationException e ) {
+			assertEquals( 1, e.getConstraintViolations().size() );
+			final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+			assertEquals( Color.class, constraintViolation.getRootBeanClass() );
+		}
+
+		tx.rollback();
+		s.close();
+	}
+
+	public void testEmbeddedCollection() {
+		Session s = openSession(  );
+		Transaction tx = s.beginTransaction();
+
+		Screen screen = new Screen();
+		PowerSupply ps = new PowerSupply();
+		screen.setPowerSupply( ps );
+		DisplayConnector conn = new DisplayConnector();
+		conn.setNumber( 0 );
+		screen.getConnectors().add( conn );
+		try {
+			s.persist( screen );
+			s.flush();
+			fail("Collection of embedded objects should be validated");
+		}
+		catch ( ConstraintViolationException e ) {
+			assertEquals( 1, e.getConstraintViolations().size() );
+			final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+			assertEquals( Screen.class, constraintViolation.getRootBeanClass() );
+			// connectors[0] should be connectors expect failure when bug is fixed in HV
+			assertEquals( "connectors[0].number", constraintViolation.getPropertyPath() );
+		}
+
+		tx.rollback();
+		s.close();
+	}
+
+	public void testAssocInEmbeddedNotValidated() {
+		Session s = openSession(  );
+		Transaction tx = s.beginTransaction();
+
+		Screen screen = new Screen();
+		screen.setStopButton( new Button() );
+		screen.getStopButton().setName( "STOOOOOP" );
+		PowerSupply ps = new PowerSupply();
+		screen.setPowerSupply( ps );
+		DisplayConnector conn = new DisplayConnector();
+		conn.setNumber( 1 );
+		screen.getConnectors().add( conn );
+		final Display display = new Display();
+		display.setBrand( "dell" );
+		conn.setDisplay( display );
+		s.persist( display );
+		s.flush();
+		try {
+			display.setBrand( null );
+			s.persist( screen );
+			s.flush();
+			fail("Collection of embedded objects should be validated");
+		}
+		catch ( ConstraintViolationException e ) {
+			assertEquals( 1, e.getConstraintViolations().size() );
+			final ConstraintViolation constraintViolation = e.getConstraintViolations().iterator().next();
+			assertEquals( Display.class, constraintViolation.getRootBeanClass() );
+		}
+
+		tx.rollback();
+		s.close();
+	}
+
+	@Override
+	protected void configure(Configuration cfg) {
+		super.configure( cfg );
+		cfg.setProperty( "hibernate.validator.autoregister_listeners", "false" );
+	}
+
+	protected Class<?>[] getMappings() {
+		return new Class<?>[] {
+				Button.class,
+				Color.class,
+				Display.class,
+				DisplayConnector.class,
+				PowerSupply.class,
+				Screen.class
+		};
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Music.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,13 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class Music {
+	@Id
+	public String name;
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/PowerSupply.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,46 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.math.BigDecimal;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Column;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class PowerSupply {
+	private Integer id;
+	private BigDecimal power;
+	private String position;
+
+	@Id @GeneratedValue
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	@Min(100) @Max(250)
+	public BigDecimal getPower() {
+		return power;
+	}
+
+	public void setPower(BigDecimal power) {
+		this.power = power;
+	}
+
+	@Column(name="fld_pos")
+	public String getPosition() {
+		return position;
+	}
+
+	public void setPosition(String position) {
+		this.position = position;
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Rock.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,13 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class Rock extends Music {
+	@NotNull
+	public Integer bit;
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Screen.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,74 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.util.Set;
+import java.util.HashSet;
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.ManyToMany;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.CascadeType;
+import javax.validation.constraints.NotNull;
+import javax.validation.Valid;
+
+import org.hibernate.annotations.CollectionOfElements;
+import org.hibernate.annotations.Cascade;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class Screen {
+	private Integer id;
+	private Button stopButton;
+	private PowerSupply powerSupply;
+	private Set<DisplayConnector> connectors = new HashSet<DisplayConnector>();
+	private Set<Color> displayColors = new HashSet<Color>();
+
+	@Id @GeneratedValue
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	//@NotNull 
+	@Valid
+	public Button getStopButton() {
+		return stopButton;
+	}
+
+	public void setStopButton(Button stopButton) {
+		this.stopButton = stopButton;
+	}
+
+	@ManyToOne(cascade = CascadeType.PERSIST) @Valid
+	@NotNull
+	public PowerSupply getPowerSupply() {
+		return powerSupply;
+	}
+
+	public void setPowerSupply(PowerSupply powerSupply) {
+		this.powerSupply = powerSupply;
+	}
+
+	@CollectionOfElements @Valid
+	public Set<DisplayConnector> getConnectors() {
+		return connectors;
+	}
+
+	public void setConnectors(Set<DisplayConnector> connectors) {
+		this.connectors = connectors;
+	}
+
+	@ManyToMany(cascade = CascadeType.PERSIST)
+	public Set<Color> getDisplayColors() {
+		return displayColors;
+	}
+
+	public void setDisplayColors(Set<Color> displayColors) {
+		this.displayColors = displayColors;
+	}
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/Tv.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,50 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import java.math.BigInteger;
+import java.math.BigDecimal;
+import java.util.Date;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Embeddable;
+import javax.validation.constraints.Future;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.Size;
+import javax.validation.constraints.NotNull;
+import javax.validation.Valid;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class Tv {
+
+	@Id
+	@Size(max = 2)
+	public String serial;
+	public int size;
+	@Size(max = 2)
+	public String name;
+	@Future
+	public Date expDate;
+	@Size(min = 0)
+	public String description;
+	@Min(1000)
+	public BigInteger lifetime;
+	@NotNull @Valid
+	public Tuner tuner;
+	@Valid
+	public Recorder recorder;
+
+	@Embeddable
+	public static class Tuner {
+		@NotNull
+		public String frequency;
+	}
+
+	@Embeddable
+	public static class Recorder {
+		@NotNull
+		public BigDecimal time;
+	}
+
+}

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/beanvalidation/TvOwner.java	2009-05-06 15:52:26 UTC (rev 16516)
@@ -0,0 +1,21 @@
+package org.hibernate.test.annotations.beanvalidation;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.ManyToOne;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+public class TvOwner {
+	@Id
+	@GeneratedValue
+	public Integer id;
+	
+	@ManyToOne
+	@NotNull
+	public Tv tv;
+}
\ No newline at end of file




More information about the hibernate-commits mailing list