HHH-6268 Bound JPA callback methods to entities, including those defined by default listeners, and added new method to Integrator that handles MetadataImplementor instead of Configuration, modifying affected classes accordingly. BeanValidationIntegrator and EnversIntegrator still need to be implemented but require other portions of metamodel to be completed first.

This commit is contained in:
JPAV 2011-06-14 13:10:12 -05:00 committed by John Verhaeg
parent 05cec5a4ba
commit adf6271594
21 changed files with 931 additions and 139 deletions

View File

@ -39,6 +39,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
@ -137,6 +138,33 @@ public class BeanValidationIntegrator implements Integrator {
);
}
/**
* {@inheritDoc}
*
* @see org.hibernate.integrator.spi.Integrator#integrate(org.hibernate.metamodel.source.MetadataImplementor, org.hibernate.engine.spi.SessionFactoryImplementor, org.hibernate.service.spi.SessionFactoryServiceRegistry)
*/
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
// Properties props = sessionFactory.getProperties();
// final Set<ValidationMode> modes = ValidationMode.getModes(props.get(MODE_PROPERTY));
// final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
// // try to locate a BV class to see if it is available on the classpath
// boolean isBeanValidationAvailable;
// try {
// classLoaderService.classForName( BV_CHECK_CLASS );
// isBeanValidationAvailable = true;
// } catch (Exception error) {
// isBeanValidationAvailable = false;
// }
// // locate the type safe activator class
// final Class typeSafeActivatorClass = loadTypeSafeActivatorClass(serviceRegistry);
// // todo : if this works out, probably better to simply alter TypeSafeActivator into a single method...
// applyRelationalConstraints(modes, isBeanValidationAvailable, typeSafeActivatorClass, props, metadata);
// applyHibernateListeners(modes, isBeanValidationAvailable, typeSafeActivatorClass, sessionFactory, serviceRegistry);
}
private Class loadTypeSafeActivatorClass(SessionFactoryServiceRegistry serviceRegistry) {
try {
return serviceRegistry.getService( ClassLoaderService.class ).classForName( ACTIVATOR_CLASS );
@ -194,6 +222,38 @@ public class BeanValidationIntegrator implements Integrator {
}
}
// private void applyRelationalConstraints( Set<ValidationMode> modes,
// boolean beanValidationAvailable,
// Class typeSafeActivatorClass,
// Properties properties,
// MetadataImplementor metadata ) {
// if (!ConfigurationHelper.getBoolean(APPLY_CONSTRAINTS, properties, true)){
// LOG.debug("Skipping application of relational constraints from legacy Hibernate Validator");
// return;
// }
// if (!(modes.contains(ValidationMode.DDL) || modes.contains(ValidationMode.AUTO))) return;
// if (!beanValidationAvailable) {
// if (modes.contains(ValidationMode.DDL))
// throw new HibernateException("Bean Validation not available in the class path but required in " + MODE_PROPERTY);
// if(modes.contains(ValidationMode.AUTO)) return; //nothing to activate
// }
// try {
// Method applyDDLMethod = typeSafeActivatorClass.getMethod(DDL_METHOD, Iterable.class, Properties.class, ClassLoaderService.class);
// try {
// applyDDLMethod.invoke(null, metadata.getEntityBindings(), properties,
// metadata.getServiceRegistry().getService(ClassLoaderService.class));
// } catch (HibernateException error) {
// throw error;
// } catch (Exception error) {
// throw new HibernateException("Error applying BeanValidation relational constraints", error);
// }
// } catch (HibernateException error) {
// throw error;
// } catch (Exception error) {
// throw new HibernateException("Unable to locate TypeSafeActivator#applyDDL method", error);
// }
// }
private void applyHibernateListeners(
Set<ValidationMode> modes,
boolean beanValidationAvailable,
@ -245,6 +305,40 @@ public class BeanValidationIntegrator implements Integrator {
}
}
// private void applyHibernateListeners( Set<ValidationMode> modes,
// boolean beanValidationAvailable,
// Class typeSafeActivatorClass,
// SessionFactoryImplementor sessionFactory,
// SessionFactoryServiceRegistry serviceRegistry ) {
// // de-activate not-null tracking at the core level when Bean Validation is present unless the user explicitly
// // asks for it
// if (sessionFactory.getProperties().getProperty(Environment.CHECK_NULLABILITY) == null)
// sessionFactory.getSettings().setCheckNullability( false );
// if (!(modes.contains( ValidationMode.CALLBACK) || modes.contains(ValidationMode.AUTO))) return;
// if (!beanValidationAvailable) {
// if (modes.contains(ValidationMode.CALLBACK))
// throw new HibernateException("Bean Validation not available in the class path but required in " + MODE_PROPERTY);
// if (modes.contains(ValidationMode.AUTO)) return; //nothing to activate
// }
// try {
// Method activateMethod = typeSafeActivatorClass.getMethod(ACTIVATE_METHOD, EventListenerRegistry.class);
// try {
// activateMethod.invoke(null, serviceRegistry.getService(EventListenerRegistry.class));
// }
// catch (HibernateException e) {
// throw e;
// }
// catch (Exception e) {
// throw new HibernateException( "Error applying BeanValidation relational constraints", e );
// }
// }
// catch (HibernateException e) {
// throw e;
// }
// catch (Exception e) {
// throw new HibernateException( "Unable to locate TypeSafeActivator#applyDDL method", e );
// }
// }
// Because the javax validation classes might not be on the runtime classpath
private static enum ValidationMode {

View File

@ -23,6 +23,15 @@
*/
package org.hibernate.cfg.beanvalidation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.Digits;
@ -33,24 +42,13 @@ import javax.validation.constraints.Size;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.PropertyDescriptor;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column;
@ -58,7 +56,7 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.jboss.logging.Logger;
/**
* @author Emmanuel Bernard
@ -97,6 +95,22 @@ class TypeSafeActivator {
listener.initialize( configuration );
}
// public static void activateBeanValidation( EventListenerRegistry listenerRegistry ) {
// final Properties properties = configuration.getProperties();
// ValidatorFactory factory = getValidatorFactory( properties );
// BeanValidationEventListener listener = new BeanValidationEventListener(
// factory, properties
// );
//
// listenerRegistry.addDuplicationStrategy( DuplicationStrategyImpl.INSTANCE );
//
// listenerRegistry.appendListeners( EventType.PRE_INSERT, listener );
// listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
// listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
//
// listener.initialize( configuration );
// }
@SuppressWarnings( {"UnusedDeclaration"})
public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties) {
ValidatorFactory factory = getValidatorFactory( properties );
@ -126,6 +140,25 @@ class TypeSafeActivator {
}
}
// public static void applyDDL( Iterable<EntityBinding> bindings,
// Properties properties,
// ClassLoaderService classLoaderService ) {
// ValidatorFactory factory = getValidatorFactory(properties);
// Class<?>[] groupsArray = new GroupsPerOperation(properties).get(GroupsPerOperation.Operation.DDL);
// Set<Class<?>> groups = new HashSet<Class<?>>(Arrays.asList(groupsArray));
// for (EntityBinding binding : bindings) {
// final String className = binding.getEntity().getClassName();
// if (className == null || className.length() == 0) continue;
// try {
// applyDDL("", binding, classLoaderService.classForName(className), factory, groups, true);
// } catch (ClassLoadingException error) {
// throw new AssertionFailure("Entity class not found", error);
// } catch (Exception error) {
// LOG.unableToApplyConstraints(className, error);
// }
// }
// }
private static void applyDDL(String prefix,
PersistentClass persistentClass,
Class<?> clazz,
@ -162,6 +195,23 @@ class TypeSafeActivator {
}
}
// private static void applyDDL( String prefix,
// EntityBinding binding,
// 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()) {
// AttributeBinding attrBinding = findAttributeBindingByName(binding, prefix + propertyDesc.getPropertyName());
// if (attrBinding != null) {
// applyConstraints(propertyDesc.getConstraintDescriptors(), attrBinding, propertyDesc, groups, activateNotNull);
// // TODO: Handle composite attributes when possible
// }
// }
// }
private static boolean applyConstraints(Set<ConstraintDescriptor<?>> constraintDescriptors,
Property property,
PropertyDescriptor propertyDesc,
@ -199,6 +249,37 @@ class TypeSafeActivator {
return hasNotNull;
}
// private static boolean applyConstraints( Set<ConstraintDescriptor<?>> constraintDescriptors,
// AttributeBinding attributeBinding,
// 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(attributeBinding, descriptor);
//
// // apply bean validation specific constraints
// applyDigits( property, descriptor );
// applySize( property, descriptor, propertyDesc );
// applyMin( property, descriptor );
// applyMax( property, descriptor );
//
// // apply hibernate validator specific constraints - we cannot import any HV specific classes though!
// // no need to check explicitly for @Range. @Range is a composed constraint using @Min and @Max which
// // will be taken care later
// applyLength( property, descriptor, propertyDesc );
//
// // 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")
@ -251,6 +332,26 @@ class TypeSafeActivator {
return hasNotNull;
}
// private static boolean applyNotNull( AttributeBinding attributeBinding,
// ConstraintDescriptor<?> descriptor ) {
// boolean hasNotNull = false;
// if (NotNull.class.equals(descriptor.getAnnotation().annotationType())) {
// if ( !( attributeBinding.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 = 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")
@ -291,8 +392,10 @@ class TypeSafeActivator {
}
/**
* Retrieve the property by path in a recursive way, including IndentifierProperty in the loop
* If propertyName is null or empty, the IdentifierProperty is returned
* @param associatedClass
* @param propertyName
* @return the property by path in a recursive way, including IdentifierProperty in the loop if propertyName is
* <code>null</code>. If propertyName is <code>null</code> or empty, the IdentifierProperty is returned
*/
private static Property findPropertyByName(PersistentClass associatedClass, String propertyName) {
Property property = null;
@ -352,6 +455,44 @@ class TypeSafeActivator {
return property;
}
// /**
// * @param entityBinding
// * @param attrName
// * @return the attribute by path in a recursive way, including EntityIdentifier in the loop if attrName is
// * <code>null</code>. If attrName is <code>null</code> or empty, the EntityIdentifier is returned
// */
// private static AttributeBinding findAttributeBindingByName( EntityBinding entityBinding,
// String attrName ) {
// AttributeBinding attrBinding = null;
// EntityIdentifier identifier = entityBinding.getHierarchyDetails().getEntityIdentifier();
// BasicAttributeBinding idAttrBinding = identifier.getValueBinding();
// String idAttrName = idAttrBinding != null ? idAttrBinding.getAttribute().getName() : null;
// try {
// if (attrName == null || attrName.length() == 0 || attrName.equals(idAttrName)) attrBinding = idAttrBinding; // default to id
// else {
// if (attrName.indexOf(idAttrName + ".") == 0) {
// attrBinding = idAttrBinding;
// attrName = attrName.substring(idAttrName.length() + 1);
// }
// for (StringTokenizer st = new StringTokenizer(attrName, "."); st.hasMoreElements();) {
// String element = st.nextToken();
// if (attrBinding == null) attrBinding = entityBinding.locateAttributeBinding(element);
// else return null; // TODO: if (attrBinding.isComposite()) ...
// }
// }
// } catch (MappingException error) {
// try {
// //if we do not find it try to check the identifier mapper
// if (!identifier.isIdentifierMapper()) return null;
// // TODO: finish once composite/embedded/component IDs get worked out
// }
// catch ( MappingException ee ) {
// return null;
// }
// }
// return attrBinding;
// }
private static ValidatorFactory getValidatorFactory(Map<Object, Object> properties) {
ValidatorFactory factory = null;
if ( properties != null ) {

View File

@ -25,6 +25,7 @@ package org.hibernate.integrator.spi;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/**
@ -56,6 +57,17 @@ public interface Integrator {
SessionFactoryServiceRegistry serviceRegistry);
/**
* Perform integration.
*
* @param metadata The metadata used to create the session factory
* @param sessionFactory The session factory being created
* @param serviceRegistry The session factory's service registry
*/
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry );
/**
* Tongue-in-cheek name for a shutdown callback.
*
* @param sessionFactory The session factory being closed.

View File

@ -23,7 +23,6 @@
*/
package org.hibernate.internal;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import java.io.IOException;
@ -116,7 +115,6 @@ import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.metamodel.binding.PluralAttributeBinding;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.AbstractPluralAttributeBinding;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
@ -167,7 +165,7 @@ import org.hibernate.type.TypeResolver;
* @author Gavin King
*/
public final class SessionFactoryImpl
implements SessionFactory, SessionFactoryImplementor {
implements SessionFactoryImplementor {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SessionFactoryImpl.class.getName());
private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator();
@ -218,7 +216,7 @@ public final class SessionFactoryImpl
SessionFactoryObserver observer) throws HibernateException {
LOG.debug( "Building session factory" );
sessionFactoryOptions = new SessionFactoryOptions() {
sessionFactoryOptions = new SessionFactoryOptions() {
private EntityNotFoundDelegate entityNotFoundDelegate;
@Override
@ -253,8 +251,8 @@ public final class SessionFactoryImpl
this,
cfg
);
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
this.dialect = this.jdbcServices.getDialect();
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
this.dialect = this.jdbcServices.getDialect();
this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
if ( observer != null ) {
this.observer.addObserver( observer );
@ -592,13 +590,12 @@ public final class SessionFactoryImpl
}
}
final IntegratorObserver integratorObserver = new IntegratorObserver();
this.observer.addObserver( integratorObserver );
for ( Integrator integrator : serviceRegistry.getService( IntegratorService.class ).getIntegrators() ) {
// TODO: add Integrator.integrate(MetadataImplementor, ...)
// integrator.integrate( cfg, this, this.serviceRegistry );
integratorObserver.integrators.add( integrator );
}
final IntegratorObserver integratorObserver = new IntegratorObserver();
this.observer.addObserver(integratorObserver);
for (Integrator integrator : serviceRegistry.getService(IntegratorService.class).getIntegrators()) {
integrator.integrate(metadata, this, this.serviceRegistry);
integratorObserver.integrators.add(integrator);
}
//Generators:
@ -987,7 +984,7 @@ public final class SessionFactoryImpl
// currently not doable though because of the resultset-ref stuff...
NativeSQLQuerySpecification spec;
if ( qd.getResultSetRef() != null ) {
ResultSetMappingDefinition definition = ( ResultSetMappingDefinition ) sqlResultSetMappings.get( qd.getResultSetRef() );
ResultSetMappingDefinition definition = sqlResultSetMappings.get( qd.getResultSetRef() );
if ( definition == null ) {
throw new MappingException( "Unable to find resultset-ref definition: " + qd.getResultSetRef() );
}
@ -1070,7 +1067,7 @@ public final class SessionFactoryImpl
}
@Override
public Reference getReference() throws NamingException {
public Reference getReference() {
// from javax.naming.Referenceable
LOG.debug( "Returning a Reference to the SessionFactory" );
return new Reference(
@ -1101,15 +1098,15 @@ public final class SessionFactoryImpl
}
public NamedQueryDefinition getNamedQuery(String queryName) {
return (NamedQueryDefinition) namedQueries.get(queryName);
return namedQueries.get(queryName);
}
public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) {
return (NamedSQLQueryDefinition) namedSqlQueries.get(queryName);
return namedSqlQueries.get(queryName);
}
public ResultSetMappingDefinition getResultSetMapping(String resultSetName) {
return (ResultSetMappingDefinition) sqlResultSetMappings.get(resultSetName);
return sqlResultSetMappings.get(resultSetName);
}
public Type getIdentifierType(String className) throws MappingException {
@ -1152,9 +1149,11 @@ public final class SessionFactoryImpl
}
/**
* Return the names of all persistent (mapped) classes that extend or implement the
* given class or interface, accounting for implicit/explicit polymorphism settings
* and excluding mapped subclasses/joined-subclasses of other classes in the result.
* @param className
* @return the names of all persistent (mapped) classes that extend or implement the
* given class or interface, accounting for implicit/explicit polymorphism settings
* and excluding mapped subclasses/joined-subclasses of other classes in the result.
* @throws MappingException
*/
public String[] getImplementors(String className) throws MappingException {
@ -1207,7 +1206,7 @@ public final class SessionFactoryImpl
}
public String getImportedClassName(String className) {
String result = (String) imports.get(className);
String result = imports.get(className);
if (result==null) {
try {
ReflectHelper.classForName( className );
@ -1252,6 +1251,7 @@ public final class SessionFactoryImpl
* be a "heavy" object memory wise after close() has been called. Thus
* it is important to not keep referencing the instance to let the garbage
* collector release the memory.
* @throws HibernateException
*/
public void close() throws HibernateException {
@ -1515,7 +1515,7 @@ public final class SessionFactoryImpl
}
public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
FilterDefinition def = ( FilterDefinition ) filters.get( filterName );
FilterDefinition def = filters.get( filterName );
if ( def == null ) {
throw new HibernateException( "No such filter configured [" + filterName + "]" );
}

View File

@ -42,6 +42,7 @@ import org.hibernate.metamodel.domain.PluralAttributeNature;
import org.hibernate.metamodel.domain.SingularAttribute;
import org.hibernate.metamodel.relational.TableSpecification;
import org.hibernate.metamodel.source.MetaAttributeContext;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.entity.EntityTuplizer;
@ -101,6 +102,8 @@ public class EntityBinding implements AttributeBindingContainer {
private Set<String> synchronizedTableNames = new HashSet<String>();
private Map<String, AttributeBinding> attributeBindingMap = new HashMap<String, AttributeBinding>();
private List<JpaCallbackClass> jpaCallbackClasses = new ArrayList<JpaCallbackClass>();
/**
* Used to instantiate the EntityBinding for an entity that is the root of an inheritance hierarchy
*
@ -591,4 +594,12 @@ public class EntityBinding implements AttributeBindingContainer {
}
return new JoinedIterable<AttributeBinding>( iterables );
}
public void setJpaCallbackClasses( List<JpaCallbackClass> jpaCallbackClasses ) {
this.jpaCallbackClasses = jpaCallbackClasses;
}
public Iterable<JpaCallbackClass> getJpaCallbackClasses() {
return jpaCallbackClasses;
}
}

View File

@ -23,19 +23,33 @@
*/
package org.hibernate.metamodel.source.annotations.entity;
import javax.persistence.AccessType;
import javax.persistence.DiscriminatorType;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.AccessType;
import javax.persistence.DiscriminatorType;
import javax.persistence.PersistenceException;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
import org.jboss.jandex.Type.Kind;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
@ -54,7 +68,9 @@ import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.source.annotations.attribute.ColumnValues;
import org.hibernate.metamodel.source.annotations.attribute.FormulaValue;
import org.hibernate.metamodel.source.annotations.xml.PseudoJpaDotNames;
import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.metamodel.source.binder.TableSource;
/**
@ -101,6 +117,8 @@ public class EntityClass extends ConfiguredClass {
private boolean isDiscriminatorIncludedInSql = true;
List<JpaCallbackClass> jpaCallbackClasses = new ArrayList<JpaCallbackClass>();
public EntityClass(
ClassInfo classInfo,
EntityClass parent,
@ -136,8 +154,152 @@ public class EntityClass extends ConfiguredClass {
processProxyGeneration();
processDiscriminator();
// Bind default JPA entity listener callbacks (unless excluded), using superclasses first (unless excluded)
if (JandexHelper.getSingleAnnotation(classInfo, JPADotNames.EXCLUDE_DEFAULT_LISTENERS) == null) {
for (AnnotationInstance callbackClasses : context.getIndex().getAnnotations(PseudoJpaDotNames.DEFAULT_ENTITY_LISTENERS)) {
for (Type callbackClass : callbackClasses.value().asClassArray()) {
String callbackClassName = callbackClass.name().toString();
try {
processDefaultJpaCallbacks(callbackClassName, context.getClassInfo(callbackClassName), context);
} catch (PersistenceException error) {
throw new PersistenceException(error.getMessage() + "default entity listener " + callbackClassName);
}
}
}
}
// Bind JPA entity listener callbacks, using superclasses first (unless excluded)
for (AnnotationInstance callbackClasses : classInfo.annotations().get(JPADotNames.ENTITY_LISTENERS)) {
for (Type callbackClass : callbackClasses.value().asClassArray()) {
String callbackClassName = callbackClass.name().toString();
try {
processJpaCallbacks(callbackClassName, context.getClassInfo(callbackClassName), true, context);
} catch (PersistenceException error) {
throw new PersistenceException(error.getMessage() + "entity listener " + callbackClassName);
}
}
}
// Bind JPA entity.mapped superclass callbacks, using superclasses first (unless excluded)
try {
processJpaCallbacks(getName(), classInfo, false, context);
} catch (PersistenceException error) {
throw new PersistenceException(error.getMessage() + "entity/mapped superclass " + classInfo.name().toString());
}
}
private void processDefaultJpaCallbacks( String instanceCallbackClassName,
ClassInfo callbackClassInfo,
AnnotationBindingContext context ) {
// Process superclass first if available and not excluded
if (JandexHelper.getSingleAnnotation(callbackClassInfo, JPADotNames.EXCLUDE_SUPERCLASS_LISTENERS) != null) {
DotName superName = callbackClassInfo.superName();
if (superName != null)
processDefaultJpaCallbacks(instanceCallbackClassName, context.getClassInfo(superName.toString()), context);
}
String callbackClassName = callbackClassInfo.name().toString();
Map<Class<?>, String> callbacksByType = new HashMap<Class<?>, String>();
createDefaultCallback(PrePersist.class, PseudoJpaDotNames.DEFAULT_PRE_PERSIST, callbackClassName, callbacksByType,
context);
createDefaultCallback(PreRemove.class, PseudoJpaDotNames.DEFAULT_PRE_REMOVE, callbackClassName, callbacksByType,
context);
createDefaultCallback(PreUpdate.class, PseudoJpaDotNames.DEFAULT_PRE_UPDATE, callbackClassName, callbacksByType,
context);
createDefaultCallback(PostLoad.class, PseudoJpaDotNames.DEFAULT_POST_LOAD, callbackClassName, callbacksByType,
context);
createDefaultCallback(PostPersist.class, PseudoJpaDotNames.DEFAULT_POST_PERSIST, callbackClassName, callbacksByType,
context);
createDefaultCallback(PostRemove.class, PseudoJpaDotNames.DEFAULT_POST_REMOVE, callbackClassName, callbacksByType,
context);
createDefaultCallback(PostUpdate.class, PseudoJpaDotNames.DEFAULT_POST_UPDATE, callbackClassName, callbacksByType,
context);
if (!callbacksByType.isEmpty())
jpaCallbackClasses.add(new JpaCallbackClassImpl(instanceCallbackClassName, callbacksByType, true));
}
private void processJpaCallbacks( String instanceCallbackClassName,
ClassInfo callbackClassInfo,
boolean isListener,
AnnotationBindingContext context ) {
// Process superclass first if available and not excluded
if (JandexHelper.getSingleAnnotation(callbackClassInfo, JPADotNames.EXCLUDE_SUPERCLASS_LISTENERS) != null) {
DotName superName = callbackClassInfo.superName();
if (superName != null)
processJpaCallbacks(instanceCallbackClassName, context.getClassInfo(superName.toString()), isListener, context);
}
Map<Class<?>, String> callbacksByType = new HashMap<Class<?>, String>();
createCallback(PrePersist.class, JPADotNames.PRE_PERSIST, callbacksByType, callbackClassInfo, isListener);
createCallback(PreRemove.class, JPADotNames.PRE_REMOVE, callbacksByType, callbackClassInfo, isListener);
createCallback(PreUpdate.class, JPADotNames.PRE_UPDATE, callbacksByType, callbackClassInfo, isListener);
createCallback(PostLoad.class, JPADotNames.POST_LOAD, callbacksByType, callbackClassInfo, isListener);
createCallback(PostPersist.class, JPADotNames.POST_PERSIST, callbacksByType, callbackClassInfo, isListener);
createCallback(PostRemove.class, JPADotNames.POST_REMOVE, callbacksByType, callbackClassInfo, isListener);
createCallback(PostUpdate.class, JPADotNames.POST_UPDATE, callbacksByType, callbackClassInfo, isListener);
if (!callbacksByType.isEmpty())
jpaCallbackClasses.add(new JpaCallbackClassImpl(instanceCallbackClassName, callbacksByType, isListener));
}
private void createDefaultCallback( Class callbackTypeClass,
DotName callbackTypeName,
String callbackClassName,
Map<Class<?>, String> callbacksByClass,
AnnotationBindingContext context ) {
for (AnnotationInstance callback : context.getIndex().getAnnotations(callbackTypeName)) {
MethodInfo methodInfo = (MethodInfo)callback.target();
validateMethod(methodInfo, callbackTypeClass, callbacksByClass, true);
if (methodInfo.declaringClass().name().equals(callbackClassName)) {
if (methodInfo.args().length != 1)
throw new PersistenceException("Callback method " + methodInfo.name() +
" must have exactly one argument defined as either Object or " +
getEntityName() + " in ");
createCallback(callbackTypeClass, callbacksByClass, methodInfo);
}
}
}
private void createCallback( Class callbackTypeClass,
DotName callbackTypeName,
Map<Class<?>, String> callbacksByClass,
ClassInfo callbackClassInfo,
boolean isListener ) {
Map<DotName, List<AnnotationInstance>> annotations = callbackClassInfo.annotations();
for (AnnotationInstance callback : annotations.get(callbackTypeName)) {
MethodInfo methodInfo = (MethodInfo)callback.target();
validateMethod(methodInfo, callbackTypeClass, callbacksByClass, isListener);
createCallback(callbackTypeClass, callbacksByClass, methodInfo);
}
}
private void createCallback( Class callbackTypeClass,
Map<Class<?>, String> callbacksByClass,
MethodInfo methodInfo ) {
callbacksByClass.put(callbackTypeClass, methodInfo.name());
}
private void validateMethod( MethodInfo methodInfo,
Class callbackTypeClass,
Map<Class<?>, String> callbacksByClass,
boolean isListener) {
if (methodInfo.returnType().kind() != Kind.VOID)
throw new PersistenceException("Callback method " + methodInfo.name() + " must have a void return type in ");
if (Modifier.isStatic(methodInfo.flags()) || Modifier.isFinal(methodInfo.flags()))
throw new PersistenceException("Callback method " + methodInfo.name() + " must not be static or final in ");
Type[] argTypes = methodInfo.args();
if (isListener) {
if (argTypes.length != 1)
throw new PersistenceException("Callback method " + methodInfo.name() + " must have exactly one argument in ");
String argTypeName = argTypes[0].name().toString();
if (!argTypeName.equals(Object.class.getName()) && !argTypeName.equals(getName()))
throw new PersistenceException("The argument for callback method " + methodInfo.name() +
" must be defined as either Object or " + getEntityName() + " in ");
} else if (argTypes.length != 0)
throw new PersistenceException("Callback method " + methodInfo.name() + " must have no arguments in ");
if (callbacksByClass.containsKey(callbackTypeClass))
throw new PersistenceException("Only one method may be annotated as a " + callbackTypeClass.getSimpleName() +
" callback method in ");
}
public ColumnValues getDiscriminatorColumnValues() {
return discriminatorColumnValues;
}
@ -740,4 +902,55 @@ public class EntityClass extends ConfiguredClass {
public String getDiscriminatorMatchValue() {
return discriminatorMatchValue;
}
public List<JpaCallbackClass> getJpaCallbackClasses() {
return jpaCallbackClasses;
}
// Process JPA callbacks, in superclass-first order (unless superclasses are excluded), using default listeners first
// (unless default listeners are excluded), then entity listeners, and finally the entity/mapped superclass itself
private class JpaCallbackClassImpl implements JpaCallbackClass {
private final Map<Class<?>, String> callbacksByType;
private final String name;
private final boolean isListener;
private JpaCallbackClassImpl( String name,
Map<Class<?>, String> callbacksByType,
boolean isListener ) {
this.name = name;
this.callbacksByType = callbacksByType;
this.isListener = isListener;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.metamodel.source.binder.JpaCallbackClass#getCallback(java.lang.Class)
*/
@Override
public String getCallback( Class<?> callbackAnnotationClass ) {
return callbacksByType.get(callbackAnnotationClass);
}
/**
* {@inheritDoc}
*
* @see org.hibernate.metamodel.source.binder.JpaCallbackClass#getName()
*/
@Override
public String getName() {
return name;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.metamodel.source.binder.JpaCallbackClass#isListener()
*/
@Override
public boolean isListener() {
return isListener;
}
}
}

View File

@ -39,6 +39,7 @@ import org.hibernate.metamodel.source.annotations.attribute.ToOneAttributeSource
import org.hibernate.metamodel.source.binder.AttributeSource;
import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.EntitySource;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.metamodel.source.binder.MetaAttributeSource;
import org.hibernate.metamodel.source.binder.SubclassEntitySource;
import org.hibernate.metamodel.source.binder.TableSource;
@ -204,6 +205,16 @@ public class EntitySourceImpl implements EntitySource {
return entityClass.getConstraintSources();
}
/**
* {@inheritDoc}
*
* @see org.hibernate.metamodel.source.binder.EntitySource#getJpaCallbackClasses()
*/
@Override
public List<JpaCallbackClass> getJpaCallbackClasses() {
return entityClass.getJpaCallbackClasses();
}
@Override
public Iterable<TableSource> getSecondaryTables() {
return entityClass.getSecondaryTableSources();

View File

@ -30,7 +30,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.cfg.NotYetImplementedException;
@ -77,7 +76,7 @@ import org.hibernate.tuple.entity.EntityTuplizer;
/**
* The common binder shared between annotations and {@code hbm.xml} processing.
* <p/>
* The API consists of {@link #Binder} and {@link #processEntityHierarchy}
* The API consists of {@link #Binder(MetadataImplementor, List)} and {@link #processEntityHierarchy(EntityHierarchy)}
*
* @author Steve Ebersole
* @author Hardy Ferentschik
@ -265,6 +264,8 @@ public class Binder {
entityBinding.addSynchronizedTableNames( entitySource.getSynchronizedTableNames() );
}
entityBinding.setJpaCallbackClasses(entitySource.getJpaCallbackClasses());
return entityBinding;
}
@ -307,14 +308,17 @@ public class Binder {
switch ( entitySource.getIdentifierSource().getNature() ) {
case SIMPLE: {
bindSimpleIdentifier( (SimpleIdentifierSource) entitySource.getIdentifierSource(), entityBinding );
break;
}
case AGGREGATED_COMPOSITE: {
// composite id with an actual component class
break;
}
case COMPOSITE: {
// what we used to term an "embedded composite identifier", which is not tobe confused with the JPA
// term embedded. Specifically a composite id where there is no component class, though there may
// be a @IdClass :/
break;
}
}
}

View File

@ -24,7 +24,9 @@
package org.hibernate.metamodel.source.binder;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import org.hibernate.metamodel.binding.CustomSQL;
import org.hibernate.metamodel.source.LocalBindingContext;
import org.hibernate.metamodel.source.Origin;
@ -35,6 +37,7 @@ import org.hibernate.metamodel.source.Origin;
* @author Steve Ebersole
*/
public interface EntitySource extends SubclassEntityContainer, AttributeSourceContainer {
/**
* Obtain the origin of this source.
*
@ -203,4 +206,10 @@ public interface EntitySource extends SubclassEntityContainer, AttributeSourceCo
* @return returns the source information for constraints defined on the table
*/
public Iterable<ConstraintSource> getConstraints();
/**
* @return the list of classes (this {@link Entity entity}/{@link MappedSuperclass mapped superclass}, or
* {@link EntityListeners entity listeners}) that define JPA callbacks for this entity/mapped superclass.
*/
List<JpaCallbackClass> getJpaCallbackClasses();
}

View File

@ -0,0 +1,59 @@
/*
* 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.metamodel.source.binder;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
public interface JpaCallbackClass {
/**
* @param callbackAnnotationClass {@link PrePersist}, {@link PreRemove}, {@link PreUpdate}, {@link PostLoad},
* {@link PostPersist}, {@link PostRemove}, or {@link PostUpdate}
* @return the name of the JPA callback method defined for the associated {@link Entity entity} or {@link MappedSuperclass
* mapped superclass} and for the supplied callback annotation class.
*/
String getCallback( Class<?> callbackAnnotationClass );
/**
* @return the name of the instantiated container where the JPA callbacks for the associated {@link Entity entity} or
* {@link MappedSuperclass mapped superclass} are defined. This can be either the entity/mapped superclass itself or an
* {@link EntityListeners entity listener}.
*/
String getName();
/**
* @return <code>true</code> if this callback class represents callbacks defined within an {@link EntityListeners entity
* listener}.
*/
boolean isListener();
}

View File

@ -25,7 +25,6 @@ package org.hibernate.metamodel.source.hbm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.hibernate.AssertionFailure;
@ -37,6 +36,7 @@ import org.hibernate.metamodel.source.Origin;
import org.hibernate.metamodel.source.binder.AttributeSource;
import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.EntitySource;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.metamodel.source.binder.MetaAttributeSource;
import org.hibernate.metamodel.source.binder.SubclassEntitySource;
import org.hibernate.metamodel.source.binder.TableSource;
@ -305,4 +305,14 @@ public abstract class AbstractEntitySourceImpl implements EntitySource {
public Iterable<TableSource> getSecondaryTables() {
return Collections.emptySet();
}
/**
* {@inheritDoc}
*
* @see org.hibernate.metamodel.source.binder.EntitySource#getJpaCallbackClasses()
*/
@Override
public List<JpaCallbackClass> getJpaCallbackClasses() {
return Collections.EMPTY_LIST;
}
}

View File

@ -1,13 +1,17 @@
<?xml version="1.0"?>
<!--
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
-->
<hibernate-mapping package="org.hibernate.test.event.collection.values">
<hibernate-mapping
xmlns="http://www.hibernate.org/xsd/hibernate-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-mapping
/Users/jpav/hibernate/hibernate-core/hibernate-core/src/main/resources/org/hibernate/hibernate-mapping-4.0.xsd"
package="org.hibernate.test.event.collection.values">
<class name="ParentWithCollectionOfValues" table="PARENT">
<id name="id" column="ID" type="long">

View File

@ -36,6 +36,7 @@ import org.hibernate.event.spi.DeleteEventListener;
import org.hibernate.event.spi.EventType;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.integrator.spi.IntegratorService;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.internal.BasicServiceRegistryImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
@ -55,11 +56,13 @@ public class CallbackTest extends BaseCoreFunctionalTestCase {
private TestingObserver observer = new TestingObserver();
private TestingListener listener = new TestingListener();
public String[] getMappings() {
@Override
public String[] getMappings() {
return NO_MAPPINGS;
}
public void configure(Configuration cfg) {
@Override
public void configure(Configuration cfg) {
cfg.setSessionFactoryObserver( observer );
}
@ -68,17 +71,27 @@ public class CallbackTest extends BaseCoreFunctionalTestCase {
super.applyServices( serviceRegistry );
serviceRegistry.getService( IntegratorService.class ).addIntegrator(
new Integrator() {
@Override
@Override
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
serviceRegistry.getService( EventListenerRegistry.class ).setListeners(
EventType.DELETE, listener
);
listener.initialize( configuration );
integrate(serviceRegistry);
}
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
integrate(serviceRegistry);
}
private void integrate( SessionFactoryServiceRegistry serviceRegistry ) {
serviceRegistry.getService( EventListenerRegistry.class ).setListeners(EventType.DELETE, listener);
listener.initialize();
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
@ -116,7 +129,7 @@ public class CallbackTest extends BaseCoreFunctionalTestCase {
private int initCount = 0;
private int destoryCount = 0;
public void initialize(Configuration cfg) {
public void initialize() {
initCount++;
}

View File

@ -32,7 +32,7 @@ import org.hibernate.integrator.spi.IntegratorService;
import org.hibernate.service.internal.BasicServiceRegistryImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.junit.Test;
import org.hibernate.testing.FailureExpected;
@ -54,14 +54,25 @@ public class TestCollectionInitializingDuringFlush extends BaseCoreFunctionalTes
super.applyServices( serviceRegistry );
serviceRegistry.getService( IntegratorService.class ).addIntegrator(
new Integrator() {
@Override
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
serviceRegistry.getService( EventListenerRegistry.class )
.getEventListenerGroup( EventType.PRE_UPDATE )
.appendListener( new InitializingPreUpdateEventListener() );
integrate(serviceRegistry);
}
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
integrate(serviceRegistry);
}
private void integrate( SessionFactoryServiceRegistry serviceRegistry ) {
serviceRegistry.getService(EventListenerRegistry.class).getEventListenerGroup(EventType.PRE_UPDATE)
.appendListener(new InitializingPreUpdateEventListener());
}
@Override

View File

@ -41,6 +41,7 @@ import org.hibernate.event.internal.DefaultFlushEventListener;
import org.hibernate.event.internal.DefaultPersistEventListener;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.util.collections.IdentityMap;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.integrator.spi.IntegratorService;
@ -73,21 +74,33 @@ public abstract class AbstractJPATest extends BaseCoreFunctionalTestCase {
super.applyServices( serviceRegistry );
serviceRegistry.getService( IntegratorService.class ).addIntegrator(
new Integrator() {
@Override
@Override
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
eventListenerRegistry.setListeners( EventType.PERSIST, buildPersistEventListeners() );
eventListenerRegistry.setListeners(
EventType.PERSIST_ONFLUSH, buildPersisOnFlushEventListeners()
);
eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, buildAutoFlushEventListeners() );
eventListenerRegistry.setListeners( EventType.FLUSH, buildFlushEventListeners() );
eventListenerRegistry.setListeners( EventType.FLUSH_ENTITY, buildFlushEntityEventListeners() );
integrate(serviceRegistry);
}
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
integrate(serviceRegistry);
}
private void integrate( SessionFactoryServiceRegistry serviceRegistry ) {
EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
eventListenerRegistry.setListeners( EventType.PERSIST, buildPersistEventListeners() );
eventListenerRegistry.setListeners(
EventType.PERSIST_ONFLUSH, buildPersisOnFlushEventListeners()
);
eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, buildAutoFlushEventListeners() );
eventListenerRegistry.setListeners( EventType.FLUSH, buildFlushEventListeners() );
eventListenerRegistry.setListeners( EventType.FLUSH_ENTITY, buildFlushEntityEventListeners() );
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
@ -107,7 +120,7 @@ public abstract class AbstractJPATest extends BaseCoreFunctionalTestCase {
private static class JPAEntityNotFoundDelegate implements EntityNotFoundDelegate {
public void handleEntityNotFound(String entityName, Serializable id) {
throw new EntityNotFoundException("Unable to find " + entityName + " with id " + id);
throw new EntityNotFoundException("Unable to find " + entityName + " with id " + id);
}
}

View File

@ -37,7 +37,7 @@ import org.hibernate.integrator.spi.IntegratorService;
import org.hibernate.service.internal.BasicServiceRegistryImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
@ -51,11 +51,13 @@ import static org.junit.Assert.fail;
*/
@SuppressWarnings( {"unchecked"})
public class EagerKeyManyToOneTest extends BaseCoreFunctionalTestCase {
public String[] getMappings() {
@Override
public String[] getMappings() {
return new String[] { "keymanytoone/bidir/component/EagerMapping.hbm.xml" };
}
public void configure(Configuration cfg) {
@Override
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
}
@ -66,16 +68,27 @@ public class EagerKeyManyToOneTest extends BaseCoreFunctionalTestCase {
serviceRegistry.getService( IntegratorService.class ).addIntegrator(
new Integrator() {
@Override
@Override
public void integrate(
Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
serviceRegistry.getService( EventListenerRegistry.class ).prependListeners(
EventType.LOAD, new CustomLoadListener()
);
integrate(serviceRegistry);
}
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
integrate(serviceRegistry);
}
private void integrate( SessionFactoryServiceRegistry serviceRegistry ) {
serviceRegistry.getService( EventListenerRegistry.class ).prependListeners(EventType.LOAD,
new CustomLoadListener());
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
@ -184,7 +197,8 @@ public class EagerKeyManyToOneTest extends BaseCoreFunctionalTestCase {
private static class CustomLoadListener extends DefaultLoadEventListener {
private int internalLoadCount = 0;
public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException {
@Override
public void onLoad(LoadEvent event, LoadType loadType) throws HibernateException {
if ( LoadEventListener.INTERNAL_LOAD_EAGER.getName().equals( loadType.getName() ) ) {
internalLoadCount++;
if ( internalLoadCount > 10 ) {

View File

@ -35,13 +35,15 @@ import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XMethod;
import org.hibernate.ejb.internal.EntityManagerMessageLogger;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/**
* @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
@ -69,7 +71,7 @@ public final class CallbackResolver {
public static Callback[] resolveCallback(XClass beanClass, Class annotation, ReflectionManager reflectionManager) {
List<Callback> callbacks = new ArrayList<Callback>();
List<String> callbacksMethodNames = new ArrayList<String>(); //used to track overriden methods
List<String> callbacksMethodNames = new ArrayList<String>(); //used to track overridden methods
List<Class> orderedListeners = new ArrayList<Class>();
XClass currentClazz = beanClass;
boolean stopListeners = false;
@ -84,7 +86,7 @@ public final class CallbackResolver {
Method method = reflectionManager.toMethod( xMethod );
final String methodName = method.getName();
if ( ! callbacksMethodNames.contains( methodName ) ) {
//overriden method, remove the superclass overriden method
//overridden method, remove the superclass overridden method
if ( callback == null ) {
callback = new BeanCallback( method );
Class returnType = method.getReturnType();
@ -145,65 +147,117 @@ public final class CallbackResolver {
if ( listener != null ) {
XClass xListener = reflectionManager.toXClass( listener );
callbacksMethodNames = new ArrayList<String>();
do {
List<XMethod> methods = xListener.getDeclaredMethods();
final int size = methods.size();
for ( int i = 0; i < size ; i++ ) {
final XMethod xMethod = methods.get( i );
if ( xMethod.isAnnotationPresent( annotation ) ) {
final Method method = reflectionManager.toMethod( xMethod );
final String methodName = method.getName();
if ( ! callbacksMethodNames.contains( methodName ) ) {
//overriden method, remove the superclass overriden method
if ( callback == null ) {
try {
callback = new ListenerCallback( method, listener.newInstance() );
}
catch (IllegalAccessException e) {
throw new PersistenceException(
"Unable to create instance of " + listener.getName()
+ " as a listener of beanClass", e
);
}
catch (InstantiationException e) {
throw new PersistenceException(
"Unable to create instance of " + listener.getName()
+ " as a listener of beanClass", e
);
}
Class returnType = method.getReturnType();
Class[] args = method.getParameterTypes();
if ( returnType != Void.TYPE || args.length != 1 ) {
throw new PersistenceException(
"Callback methods annotated in a listener bean class must return void and take one argument: " + annotation
.getName() + " - " + method
);
}
if (!method.isAccessible()) method.setAccessible(true);
LOG.debugf("Adding %s as %s callback for entity %s",
methodName,
annotation.getSimpleName(),
beanClass.getName());
callbacks.add( 0, callback ); // listeners first
List<XMethod> methods = xListener.getDeclaredMethods();
final int size = methods.size();
for ( int i = 0; i < size ; i++ ) {
final XMethod xMethod = methods.get( i );
if ( xMethod.isAnnotationPresent( annotation ) ) {
final Method method = reflectionManager.toMethod( xMethod );
final String methodName = method.getName();
if ( ! callbacksMethodNames.contains( methodName ) ) {
//overridden method, remove the superclass overridden method
if ( callback == null ) {
try {
callback = new ListenerCallback( method, listener.newInstance() );
}
else {
catch (IllegalAccessException e) {
throw new PersistenceException(
"You can only annotate one callback method with "
+ annotation.getName() + " in bean class: " + beanClass.getName() + " and callback listener: "
+ listener.getName()
"Unable to create instance of " + listener.getName()
+ " as a listener of beanClass", e
);
}
catch (InstantiationException e) {
throw new PersistenceException(
"Unable to create instance of " + listener.getName()
+ " as a listener of beanClass", e
);
}
Class returnType = method.getReturnType();
Class[] args = method.getParameterTypes();
if ( returnType != Void.TYPE || args.length != 1 ) {
throw new PersistenceException(
"Callback methods annotated in a listener bean class must return void and take one argument: " + annotation
.getName() + " - " + method
);
}
if (!method.isAccessible()) method.setAccessible(true);
LOG.debugf("Adding %s as %s callback for entity %s",
methodName,
annotation.getSimpleName(),
beanClass.getName());
callbacks.add( 0, callback ); // listeners first
}
else {
throw new PersistenceException(
"You can only annotate one callback method with "
+ annotation.getName() + " in bean class: " + beanClass.getName() + " and callback listener: "
+ listener.getName()
);
}
}
}
xListener = null; //xListener.getSuperclass();
}
while ( xListener != null );
}
}
return callbacks.toArray( new Callback[ callbacks.size() ] );
}
public static Callback[] resolveCallbacks( Class entityClass,
Class callbackAnnotationClass,
ClassLoaderService classLoaderService,
EntityBinding binding ) {
List<Callback> callbacks = new ArrayList<Callback>();
for (JpaCallbackClass jpaCallbackClass : binding.getJpaCallbackClasses()) {
Object listener = classLoaderService.classForName(jpaCallbackClass.getName());
String methodName = jpaCallbackClass.getCallback(callbackAnnotationClass);
Callback callback = jpaCallbackClass.isListener() ?
createListenerCallback(entityClass, callbackAnnotationClass, listener, methodName) :
createBeanCallback(callbackAnnotationClass, methodName);
LOG.debugf("Adding %s as %s callback for entity %s", methodName, callbackAnnotationClass.getName(),
entityClass.getName());
assert callback != null;
callbacks.add(callback);
}
return callbacks.toArray(new Callback[callbacks.size()]);
}
private static Callback createListenerCallback( Class<?> entityClass,
Class<?> callbackClass,
Object listener,
String methodName ) {
Class<?> callbackSuperclass = callbackClass.getSuperclass();
if (callbackSuperclass != null) {
Callback callback = createListenerCallback(entityClass, callbackSuperclass, listener, methodName);
if (callback != null) return callback;
}
for (Method method : callbackClass.getDeclaredMethods()) {
if (!method.getName().equals(methodName)) continue;
Class<?>[] argTypes = method.getParameterTypes();
if (argTypes.length != 1) continue;
Class<?> argType = argTypes[0];
if (argType != Object.class && argType != entityClass) continue;
if (!method.isAccessible()) method.setAccessible(true);
return new ListenerCallback(method, listener);
}
return null;
}
private static Callback createBeanCallback( Class<?> callbackClass,
String methodName ) {
Class<?> callbackSuperclass = callbackClass.getSuperclass();
if (callbackSuperclass != null) {
Callback callback = createBeanCallback(callbackSuperclass, methodName);
if (callback != null) return callback;
}
for (Method method : callbackClass.getDeclaredMethods()) {
if (!method.getName().equals(methodName)) continue;
if (method.getParameterTypes().length != 0) continue;
if (!method.isAccessible()) method.setAccessible(true);
return new BeanCallback(method);
}
return null;
}
private static void getListeners(XClass currentClazz, List<Class> orderedListeners) {
EntityListeners entityListeners = currentClazz.getAnnotation( EntityListeners.class );
if ( entityListeners != null ) {

View File

@ -35,6 +35,8 @@ import java.util.HashMap;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.service.classloading.spi.ClassLoaderService;
/**
* Keep track of all lifecycle callbacks and listeners for a given persistence unit
@ -61,6 +63,18 @@ public class EntityCallbackHandler implements Serializable {
addCallback( entity, postLoads, PostLoad.class, reflectionManager );
}
public void add( Class entity,
ClassLoaderService classLoaderService,
EntityBinding binding ) {
addCallback( entity, preCreates, PrePersist.class, classLoaderService, binding );
addCallback( entity, postCreates, PostPersist.class, classLoaderService, binding );
addCallback( entity, preRemoves, PreRemove.class, classLoaderService, binding );
addCallback( entity, postRemoves, PostRemove.class, classLoaderService, binding );
addCallback( entity, preUpdates, PreUpdate.class, classLoaderService, binding );
addCallback( entity, postUpdates, PostUpdate.class, classLoaderService, binding );
addCallback( entity, postLoads, PostLoad.class, classLoaderService, binding );
}
public boolean preCreate(Object bean) {
return callback( preCreates.get( bean.getClass() ), bean );
}
@ -102,7 +116,6 @@ public class EntityCallbackHandler implements Serializable {
}
}
private void addCallback(
XClass entity, HashMap<Class, Callback[]> map, Class annotation, ReflectionManager reflectionManager
) {
@ -110,4 +123,12 @@ public class EntityCallbackHandler implements Serializable {
callbacks = CallbackResolver.resolveCallback( entity, annotation, reflectionManager );
map.put( reflectionManager.toClass( entity ), callbacks );
}
private void addCallback( Class<?> entity,
HashMap<Class, Callback[]> map,
Class annotation,
ClassLoaderService classLoaderService,
EntityBinding binding ) {
map.put(entity, CallbackResolver.resolveCallbacks(entity, annotation, classLoaderService, binding));
}
}

View File

@ -34,15 +34,17 @@ import org.hibernate.cfg.Environment;
import org.hibernate.ejb.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreDeleteEventListener;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.secure.internal.JACCPreDeleteEventListener;
import org.hibernate.secure.internal.JACCPreInsertEventListener;
import org.hibernate.secure.internal.JACCPreLoadEventListener;
import org.hibernate.secure.internal.JACCPreUpdateEventListener;
import org.hibernate.secure.internal.JACCSecurityListener;
import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.service.classloading.spi.ClassLoadingException;
import org.hibernate.event.service.spi.DuplicationStrategy;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry;
@ -51,7 +53,7 @@ import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/**
* Prepare the HEM-specific event listeners.
*
*
* @author Steve Ebersole
*/
public class JpaIntegrator implements Integrator {
@ -163,6 +165,89 @@ public class JpaIntegrator implements Integrator {
}
}
/**
* {@inheritDoc}
*
* @see org.hibernate.integrator.spi.Integrator#integrate(org.hibernate.metamodel.source.MetadataImplementor, org.hibernate.engine.spi.SessionFactoryImplementor, org.hibernate.service.spi.SessionFactoryServiceRegistry)
*/
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
boolean isSecurityEnabled = sessionFactory.getProperties().containsKey( AvailableSettings.JACC_ENABLED );
eventListenerRegistry.addDuplicationStrategy( JPA_DUPLICATION_STRATEGY );
eventListenerRegistry.addDuplicationStrategy( JACC_DUPLICATION_STRATEGY );
// op listeners
eventListenerRegistry.setListeners( EventType.AUTO_FLUSH, EJB3AutoFlushEventListener.INSTANCE );
eventListenerRegistry.setListeners( EventType.DELETE, new EJB3DeleteEventListener() );
eventListenerRegistry.setListeners( EventType.FLUSH_ENTITY, new EJB3FlushEntityEventListener() );
eventListenerRegistry.setListeners( EventType.FLUSH, EJB3FlushEventListener.INSTANCE );
eventListenerRegistry.setListeners( EventType.MERGE, new EJB3MergeEventListener() );
eventListenerRegistry.setListeners( EventType.PERSIST, new EJB3PersistEventListener() );
eventListenerRegistry.setListeners( EventType.PERSIST_ONFLUSH, new EJB3PersistOnFlushEventListener() );
eventListenerRegistry.setListeners( EventType.SAVE, new EJB3SaveEventListener() );
eventListenerRegistry.setListeners( EventType.SAVE_UPDATE, new EJB3SaveOrUpdateEventListener() );
// pre op listeners
if ( isSecurityEnabled ) {
final String jaccContextId = sessionFactory.getProperties().getProperty( Environment.JACC_CONTEXTID );
eventListenerRegistry.prependListeners( EventType.PRE_DELETE, new JACCPreDeleteEventListener(jaccContextId) );
eventListenerRegistry.prependListeners( EventType.PRE_INSERT, new JACCPreInsertEventListener(jaccContextId) );
eventListenerRegistry.prependListeners( EventType.PRE_UPDATE, new JACCPreUpdateEventListener(jaccContextId) );
eventListenerRegistry.prependListeners( EventType.PRE_LOAD, new JACCPreLoadEventListener(jaccContextId) );
}
// post op listeners
eventListenerRegistry.prependListeners( EventType.POST_DELETE, new EJB3PostDeleteEventListener() );
eventListenerRegistry.prependListeners( EventType.POST_INSERT, new EJB3PostInsertEventListener() );
eventListenerRegistry.prependListeners( EventType.POST_LOAD, new EJB3PostLoadEventListener() );
eventListenerRegistry.prependListeners( EventType.POST_UPDATE, new EJB3PostUpdateEventListener() );
for ( Map.Entry<?,?> entry : sessionFactory.getProperties().entrySet() ) {
if ( ! String.class.isInstance( entry.getKey() ) ) {
continue;
}
final String propertyName = (String) entry.getKey();
if ( ! propertyName.startsWith( AvailableSettings.EVENT_LISTENER_PREFIX ) ) {
continue;
}
final String eventTypeName = propertyName.substring( AvailableSettings.EVENT_LISTENER_PREFIX.length() + 1 );
final EventType eventType = EventType.resolveEventTypeByName( eventTypeName );
final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
for ( String listenerImpl : ( (String) entry.getValue() ).split( " ," ) ) {
eventListenerGroup.appendListener( instantiate( listenerImpl, serviceRegistry ) );
}
}
final EntityCallbackHandler callbackHandler = new EntityCallbackHandler();
ClassLoaderService classLoaderSvc = serviceRegistry.getService(ClassLoaderService.class);
for (EntityBinding binding : metadata.getEntityBindings()) {
String name = binding.getEntity().getName(); // Should this be getClassName()?
if (name == null) {
//we can have non java class persisted by hibernate
continue;
}
try {
callbackHandler.add(classLoaderSvc.classForName(name), classLoaderSvc, binding);
} catch (ClassLoadingException error) {
throw new MappingException( "entity class not found: " + name, error );
}
}
//
// for ( EventType eventType : EventType.values() ) {
// final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType );
// for ( Object listener : eventListenerGroup.listeners() ) {
// if ( CallbackHandlerConsumer.class.isInstance( listener ) ) {
// ( (CallbackHandlerConsumer) listener ).setCallbackHandler( callbackHandler );
// }
// }
// }
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
}
@ -173,6 +258,6 @@ public class JpaIntegrator implements Integrator {
}
catch (Exception e) {
throw new HibernateException( "Could not instantiate requested listener [" + listenerImpl + "]", e );
}
}
}
}
}

View File

@ -97,7 +97,7 @@ public class AuditConfiguration {
revisionInfoQueryCreator = revInfoCfgResult.getRevisionInfoQueryCreator();
revisionInfoNumberReader = revInfoCfgResult.getRevisionInfoNumberReader();
modifiedEntityNamesReader = revInfoCfgResult.getModifiedEntityNamesReader();
auditStrategy = initializeAuditStrategy(revInfoCfgResult.getRevisionInfoClass(),
auditStrategy = initializeAuditStrategy(revInfoCfgResult.getRevisionInfoClass(),
revInfoCfgResult.getRevisionInfoTimestampData());
entCfg = new EntitiesConfigurator().configure(cfg, reflectionManager, globalCfg, auditEntCfg, auditStrategy,
revInfoCfgResult.getRevisionInfoXmlMapping(), revInfoCfgResult.getRevisionInfoRelationMapping());
@ -105,14 +105,14 @@ public class AuditConfiguration {
private AuditStrategy initializeAuditStrategy(Class<?> revisionInfoClass, PropertyData revisionInfoTimestampData) {
AuditStrategy strategy;
try {
Class<?> auditStrategyClass = Thread.currentThread().getContextClassLoader().loadClass(auditEntCfg.getAuditStrategyName());
strategy = (AuditStrategy) auditStrategyClass.newInstance();
} catch (Exception e) {
throw new MappingException(String.format("Unable to create AuditStrategy[%s] instance." , auditEntCfg.getAuditStrategyName()));
}
if (strategy instanceof ValidityAuditStrategy) {
// further initialization required
Getter revisionTimestampGetter = ReflectionTools.getGetter(revisionInfoClass, revisionInfoTimestampData);
@ -133,7 +133,7 @@ public class AuditConfiguration {
if (verCfg == null) {
verCfg = new AuditConfiguration(cfg);
cfgs.put(cfg, verCfg);
cfg.buildMappings();
}

View File

@ -32,12 +32,13 @@ import org.hibernate.cfg.Configuration;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/**
* Provides integration for Envers into Hibernate, which mainly means registering the proper event listeners.
*
*
* @author Steve Ebersole
*/
public class EnversIntegrator implements Integrator {
@ -75,4 +76,16 @@ public class EnversIntegrator implements Integrator {
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
// nothing to do afaik
}
/**
* {@inheritDoc}
*
* @see org.hibernate.integrator.spi.Integrator#integrate(org.hibernate.metamodel.source.MetadataImplementor, org.hibernate.engine.spi.SessionFactoryImplementor, org.hibernate.service.spi.SessionFactoryServiceRegistry)
*/
@Override
public void integrate( MetadataImplementor metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry ) {
// TODO: implement
}
}