/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.cfg.beanvalidation;
import java.lang.annotation.ElementType;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.validation.Path;
import javax.validation.TraversableResolver;

import org.hibernate.Hibernate;
import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;

Use Hibernate metadata to ignore cascade on entities. cascade on embeddable objects or collection of embeddable objects are accepted Also use Hibernate's native isInitialized method call.
Author:Emmanuel Bernard
/** * Use Hibernate metadata to ignore cascade on entities. * cascade on embeddable objects or collection of embeddable objects are accepted * * Also use Hibernate's native isInitialized method call. * * @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() ) { CompositeType componentType = (CompositeType) type; addAssociationsToTheSetForAllProperties( componentType.getPropertyNames(), componentType.getSubtypes(), (prefix.equals( "" ) ? name : prefix + name) + ".", factory); } } private String getStringBasedPath(Path.Node traversableProperty, Path pathToTraversableObject) { StringBuilder path = new StringBuilder( ); for ( Path.Node node : pathToTraversableObject ) { if (node.getName() != null) { path.append( node.getName() ).append( "." ); } } if ( traversableProperty.getName() == null ) { throw new AssertionFailure( "TraversableResolver being passed a traversableProperty with null name. pathToTraversableObject: " + path.toString() ); } path.append( traversableProperty.getName() ); return path.toString(); } public boolean isReachable(Object traversableObject, Path.Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) { //lazy, don't load return Hibernate.isInitialized( traversableObject ) && Hibernate.isPropertyInitialized( traversableObject, traversableProperty.getName() ); } public boolean isCascadable(Object traversableObject, Path.Node traversableProperty, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) { String path = getStringBasedPath( traversableProperty, pathToTraversableObject ); return ! associations.contains(path); } }