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.Configuration;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.classloading.spi.ClassLoaderService; import org.hibernate.service.classloading.spi.ClassLoaderService;
import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistry; 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) { private Class loadTypeSafeActivatorClass(SessionFactoryServiceRegistry serviceRegistry) {
try { try {
return serviceRegistry.getService( ClassLoaderService.class ).classForName( ACTIVATOR_CLASS ); 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( private void applyHibernateListeners(
Set<ValidationMode> modes, Set<ValidationMode> modes,
boolean beanValidationAvailable, 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 // Because the javax validation classes might not be on the runtime classpath
private static enum ValidationMode { private static enum ValidationMode {

View File

@ -23,6 +23,15 @@
*/ */
package org.hibernate.cfg.beanvalidation; 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.Validation;
import javax.validation.ValidatorFactory; import javax.validation.ValidatorFactory;
import javax.validation.constraints.Digits; import javax.validation.constraints.Digits;
@ -33,24 +42,13 @@ import javax.validation.constraints.Size;
import javax.validation.metadata.BeanDescriptor; import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor; import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.PropertyDescriptor; 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.AssertionFailure;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.MappingException; 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.event.spi.EventType;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
@ -58,7 +56,7 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.SingleTableSubclass; import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.event.service.spi.EventListenerRegistry; import org.jboss.logging.Logger;
/** /**
* @author Emmanuel Bernard * @author Emmanuel Bernard
@ -97,6 +95,22 @@ class TypeSafeActivator {
listener.initialize( configuration ); 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"}) @SuppressWarnings( {"UnusedDeclaration"})
public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties) { public static void applyDDL(Collection<PersistentClass> persistentClasses, Properties properties) {
ValidatorFactory factory = getValidatorFactory( 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, private static void applyDDL(String prefix,
PersistentClass persistentClass, PersistentClass persistentClass,
Class<?> clazz, 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, private static boolean applyConstraints(Set<ConstraintDescriptor<?>> constraintDescriptors,
Property property, Property property,
PropertyDescriptor propertyDesc, PropertyDescriptor propertyDesc,
@ -199,6 +249,37 @@ class TypeSafeActivator {
return hasNotNull; 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) { private static void applyMin(Property property, ConstraintDescriptor<?> descriptor) {
if ( Min.class.equals( descriptor.getAnnotation().annotationType() ) ) { if ( Min.class.equals( descriptor.getAnnotation().annotationType() ) ) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -251,6 +332,26 @@ class TypeSafeActivator {
return hasNotNull; 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) { private static void applyDigits(Property property, ConstraintDescriptor<?> descriptor) {
if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) { if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -291,8 +392,10 @@ class TypeSafeActivator {
} }
/** /**
* Retrieve the property by path in a recursive way, including IndentifierProperty in the loop * @param associatedClass
* If propertyName is null or empty, the IdentifierProperty is returned * @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) { private static Property findPropertyByName(PersistentClass associatedClass, String propertyName) {
Property property = null; Property property = null;
@ -352,6 +455,44 @@ class TypeSafeActivator {
return property; 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) { private static ValidatorFactory getValidatorFactory(Map<Object, Object> properties) {
ValidatorFactory factory = null; ValidatorFactory factory = null;
if ( properties != null ) { if ( properties != null ) {

View File

@ -25,6 +25,7 @@ package org.hibernate.integrator.spi;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry; import org.hibernate.service.spi.SessionFactoryServiceRegistry;
/** /**
@ -56,6 +57,17 @@ public interface Integrator {
SessionFactoryServiceRegistry serviceRegistry); 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. * Tongue-in-cheek name for a shutdown callback.
* *
* @param sessionFactory The session factory being closed. * @param sessionFactory The session factory being closed.

View File

@ -23,7 +23,6 @@
*/ */
package org.hibernate.internal; package org.hibernate.internal;
import javax.naming.NamingException;
import javax.naming.Reference; import javax.naming.Reference;
import javax.naming.StringRefAddr; import javax.naming.StringRefAddr;
import java.io.IOException; import java.io.IOException;
@ -116,7 +115,6 @@ import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.metamodel.binding.PluralAttributeBinding; import org.hibernate.metamodel.binding.PluralAttributeBinding;
import org.hibernate.metamodel.source.MetadataImplementor; import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.AbstractPluralAttributeBinding;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Loadable;
@ -167,7 +165,7 @@ import org.hibernate.type.TypeResolver;
* @author Gavin King * @author Gavin King
*/ */
public final class SessionFactoryImpl public final class SessionFactoryImpl
implements SessionFactory, SessionFactoryImplementor { implements SessionFactoryImplementor {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SessionFactoryImpl.class.getName()); private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SessionFactoryImpl.class.getName());
private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator(); private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator();
@ -218,7 +216,7 @@ public final class SessionFactoryImpl
SessionFactoryObserver observer) throws HibernateException { SessionFactoryObserver observer) throws HibernateException {
LOG.debug( "Building session factory" ); LOG.debug( "Building session factory" );
sessionFactoryOptions = new SessionFactoryOptions() { sessionFactoryOptions = new SessionFactoryOptions() {
private EntityNotFoundDelegate entityNotFoundDelegate; private EntityNotFoundDelegate entityNotFoundDelegate;
@Override @Override
@ -253,8 +251,8 @@ public final class SessionFactoryImpl
this, this,
cfg cfg
); );
this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class ); this.jdbcServices = this.serviceRegistry.getService( JdbcServices.class );
this.dialect = this.jdbcServices.getDialect(); this.dialect = this.jdbcServices.getDialect();
this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() ); this.sqlFunctionRegistry = new SQLFunctionRegistry( getDialect(), cfg.getSqlFunctions() );
if ( observer != null ) { if ( observer != null ) {
this.observer.addObserver( observer ); this.observer.addObserver( observer );
@ -592,13 +590,12 @@ public final class SessionFactoryImpl
} }
} }
final IntegratorObserver integratorObserver = new IntegratorObserver(); final IntegratorObserver integratorObserver = new IntegratorObserver();
this.observer.addObserver( integratorObserver ); this.observer.addObserver(integratorObserver);
for ( Integrator integrator : serviceRegistry.getService( IntegratorService.class ).getIntegrators() ) { for (Integrator integrator : serviceRegistry.getService(IntegratorService.class).getIntegrators()) {
// TODO: add Integrator.integrate(MetadataImplementor, ...) integrator.integrate(metadata, this, this.serviceRegistry);
// integrator.integrate( cfg, this, this.serviceRegistry ); integratorObserver.integrators.add(integrator);
integratorObserver.integrators.add( integrator ); }
}
//Generators: //Generators:
@ -987,7 +984,7 @@ public final class SessionFactoryImpl
// currently not doable though because of the resultset-ref stuff... // currently not doable though because of the resultset-ref stuff...
NativeSQLQuerySpecification spec; NativeSQLQuerySpecification spec;
if ( qd.getResultSetRef() != null ) { if ( qd.getResultSetRef() != null ) {
ResultSetMappingDefinition definition = ( ResultSetMappingDefinition ) sqlResultSetMappings.get( qd.getResultSetRef() ); ResultSetMappingDefinition definition = sqlResultSetMappings.get( qd.getResultSetRef() );
if ( definition == null ) { if ( definition == null ) {
throw new MappingException( "Unable to find resultset-ref definition: " + qd.getResultSetRef() ); throw new MappingException( "Unable to find resultset-ref definition: " + qd.getResultSetRef() );
} }
@ -1070,7 +1067,7 @@ public final class SessionFactoryImpl
} }
@Override @Override
public Reference getReference() throws NamingException { public Reference getReference() {
// from javax.naming.Referenceable // from javax.naming.Referenceable
LOG.debug( "Returning a Reference to the SessionFactory" ); LOG.debug( "Returning a Reference to the SessionFactory" );
return new Reference( return new Reference(
@ -1101,15 +1098,15 @@ public final class SessionFactoryImpl
} }
public NamedQueryDefinition getNamedQuery(String queryName) { public NamedQueryDefinition getNamedQuery(String queryName) {
return (NamedQueryDefinition) namedQueries.get(queryName); return namedQueries.get(queryName);
} }
public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) { public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) {
return (NamedSQLQueryDefinition) namedSqlQueries.get(queryName); return namedSqlQueries.get(queryName);
} }
public ResultSetMappingDefinition getResultSetMapping(String resultSetName) { public ResultSetMappingDefinition getResultSetMapping(String resultSetName) {
return (ResultSetMappingDefinition) sqlResultSetMappings.get(resultSetName); return sqlResultSetMappings.get(resultSetName);
} }
public Type getIdentifierType(String className) throws MappingException { 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 * @param className
* given class or interface, accounting for implicit/explicit polymorphism settings * @return the names of all persistent (mapped) classes that extend or implement the
* and excluding mapped subclasses/joined-subclasses of other classes in the result. * 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 { public String[] getImplementors(String className) throws MappingException {
@ -1207,7 +1206,7 @@ public final class SessionFactoryImpl
} }
public String getImportedClassName(String className) { public String getImportedClassName(String className) {
String result = (String) imports.get(className); String result = imports.get(className);
if (result==null) { if (result==null) {
try { try {
ReflectHelper.classForName( className ); ReflectHelper.classForName( className );
@ -1252,6 +1251,7 @@ public final class SessionFactoryImpl
* be a "heavy" object memory wise after close() has been called. Thus * 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 * it is important to not keep referencing the instance to let the garbage
* collector release the memory. * collector release the memory.
* @throws HibernateException
*/ */
public void close() throws HibernateException { public void close() throws HibernateException {
@ -1515,7 +1515,7 @@ public final class SessionFactoryImpl
} }
public FilterDefinition getFilterDefinition(String filterName) throws HibernateException { public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
FilterDefinition def = ( FilterDefinition ) filters.get( filterName ); FilterDefinition def = filters.get( filterName );
if ( def == null ) { if ( def == null ) {
throw new HibernateException( "No such filter configured [" + filterName + "]" ); 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.domain.SingularAttribute;
import org.hibernate.metamodel.relational.TableSpecification; import org.hibernate.metamodel.relational.TableSpecification;
import org.hibernate.metamodel.source.MetaAttributeContext; import org.hibernate.metamodel.source.MetaAttributeContext;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.entity.EntityTuplizer; import org.hibernate.tuple.entity.EntityTuplizer;
@ -101,6 +102,8 @@ public class EntityBinding implements AttributeBindingContainer {
private Set<String> synchronizedTableNames = new HashSet<String>(); private Set<String> synchronizedTableNames = new HashSet<String>();
private Map<String, AttributeBinding> attributeBindingMap = new HashMap<String, AttributeBinding>(); 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 * 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 ); 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; package org.hibernate.metamodel.source.annotations.entity;
import javax.persistence.AccessType; import java.lang.reflect.Modifier;
import javax.persistence.DiscriminatorType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; 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.AnnotationInstance;
import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName; 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.AnnotationException;
import org.hibernate.MappingException; 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.JandexHelper;
import org.hibernate.metamodel.source.annotations.attribute.ColumnValues; import org.hibernate.metamodel.source.annotations.attribute.ColumnValues;
import org.hibernate.metamodel.source.annotations.attribute.FormulaValue; 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.ConstraintSource;
import org.hibernate.metamodel.source.binder.JpaCallbackClass;
import org.hibernate.metamodel.source.binder.TableSource; import org.hibernate.metamodel.source.binder.TableSource;
/** /**
@ -101,6 +117,8 @@ public class EntityClass extends ConfiguredClass {
private boolean isDiscriminatorIncludedInSql = true; private boolean isDiscriminatorIncludedInSql = true;
List<JpaCallbackClass> jpaCallbackClasses = new ArrayList<JpaCallbackClass>();
public EntityClass( public EntityClass(
ClassInfo classInfo, ClassInfo classInfo,
EntityClass parent, EntityClass parent,
@ -136,8 +154,152 @@ public class EntityClass extends ConfiguredClass {
processProxyGeneration(); processProxyGeneration();
processDiscriminator(); 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() { public ColumnValues getDiscriminatorColumnValues() {
return discriminatorColumnValues; return discriminatorColumnValues;
} }
@ -740,4 +902,55 @@ public class EntityClass extends ConfiguredClass {
public String getDiscriminatorMatchValue() { public String getDiscriminatorMatchValue() {
return discriminatorMatchValue; 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.AttributeSource;
import org.hibernate.metamodel.source.binder.ConstraintSource; import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.EntitySource; 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.MetaAttributeSource;
import org.hibernate.metamodel.source.binder.SubclassEntitySource; import org.hibernate.metamodel.source.binder.SubclassEntitySource;
import org.hibernate.metamodel.source.binder.TableSource; import org.hibernate.metamodel.source.binder.TableSource;
@ -204,6 +205,16 @@ public class EntitySourceImpl implements EntitySource {
return entityClass.getConstraintSources(); return entityClass.getConstraintSources();
} }
/**
* {@inheritDoc}
*
* @see org.hibernate.metamodel.source.binder.EntitySource#getJpaCallbackClasses()
*/
@Override
public List<JpaCallbackClass> getJpaCallbackClasses() {
return entityClass.getJpaCallbackClasses();
}
@Override @Override
public Iterable<TableSource> getSecondaryTables() { public Iterable<TableSource> getSecondaryTables() {
return entityClass.getSecondaryTableSources(); return entityClass.getSecondaryTableSources();

View File

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

View File

@ -24,7 +24,9 @@
package org.hibernate.metamodel.source.binder; package org.hibernate.metamodel.source.binder;
import java.util.List; 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.binding.CustomSQL;
import org.hibernate.metamodel.source.LocalBindingContext; import org.hibernate.metamodel.source.LocalBindingContext;
import org.hibernate.metamodel.source.Origin; import org.hibernate.metamodel.source.Origin;
@ -35,6 +37,7 @@ import org.hibernate.metamodel.source.Origin;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface EntitySource extends SubclassEntityContainer, AttributeSourceContainer { public interface EntitySource extends SubclassEntityContainer, AttributeSourceContainer {
/** /**
* Obtain the origin of this source. * 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 * @return returns the source information for constraints defined on the table
*/ */
public Iterable<ConstraintSource> getConstraints(); 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.List; import java.util.List;
import org.hibernate.AssertionFailure; 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.AttributeSource;
import org.hibernate.metamodel.source.binder.ConstraintSource; import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.EntitySource; 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.MetaAttributeSource;
import org.hibernate.metamodel.source.binder.SubclassEntitySource; import org.hibernate.metamodel.source.binder.SubclassEntitySource;
import org.hibernate.metamodel.source.binder.TableSource; import org.hibernate.metamodel.source.binder.TableSource;
@ -305,4 +305,14 @@ public abstract class AbstractEntitySourceImpl implements EntitySource {
public Iterable<TableSource> getSecondaryTables() { public Iterable<TableSource> getSecondaryTables() {
return Collections.emptySet(); 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"?> <?xml version="1.0"?>
<!--
<!DOCTYPE hibernate-mapping PUBLIC <!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN" "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> "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"> <class name="ParentWithCollectionOfValues" table="PARENT">
<id name="id" column="ID" type="long"> <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.event.spi.EventType;
import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.Integrator;
import org.hibernate.integrator.spi.IntegratorService; import org.hibernate.integrator.spi.IntegratorService;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.internal.BasicServiceRegistryImpl; import org.hibernate.service.internal.BasicServiceRegistryImpl;
import org.hibernate.service.spi.SessionFactoryServiceRegistry; import org.hibernate.service.spi.SessionFactoryServiceRegistry;
@ -55,11 +56,13 @@ public class CallbackTest extends BaseCoreFunctionalTestCase {
private TestingObserver observer = new TestingObserver(); private TestingObserver observer = new TestingObserver();
private TestingListener listener = new TestingListener(); private TestingListener listener = new TestingListener();
public String[] getMappings() { @Override
public String[] getMappings() {
return NO_MAPPINGS; return NO_MAPPINGS;
} }
public void configure(Configuration cfg) { @Override
public void configure(Configuration cfg) {
cfg.setSessionFactoryObserver( observer ); cfg.setSessionFactoryObserver( observer );
} }
@ -68,17 +71,27 @@ public class CallbackTest extends BaseCoreFunctionalTestCase {
super.applyServices( serviceRegistry ); super.applyServices( serviceRegistry );
serviceRegistry.getService( IntegratorService.class ).addIntegrator( serviceRegistry.getService( IntegratorService.class ).addIntegrator(
new Integrator() { new Integrator() {
@Override
@Override
public void integrate( public void integrate(
Configuration configuration, Configuration configuration,
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) { SessionFactoryServiceRegistry serviceRegistry) {
serviceRegistry.getService( EventListenerRegistry.class ).setListeners( integrate(serviceRegistry);
EventType.DELETE, listener
);
listener.initialize( configuration );
} }
@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 @Override
public void disintegrate( public void disintegrate(
SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
@ -116,7 +129,7 @@ public class CallbackTest extends BaseCoreFunctionalTestCase {
private int initCount = 0; private int initCount = 0;
private int destoryCount = 0; private int destoryCount = 0;
public void initialize(Configuration cfg) { public void initialize() {
initCount++; initCount++;
} }

View File

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

View File

@ -41,6 +41,7 @@ import org.hibernate.event.internal.DefaultFlushEventListener;
import org.hibernate.event.internal.DefaultPersistEventListener; import org.hibernate.event.internal.DefaultPersistEventListener;
import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.util.collections.IdentityMap; import org.hibernate.internal.util.collections.IdentityMap;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.integrator.spi.IntegratorService; import org.hibernate.integrator.spi.IntegratorService;
@ -73,21 +74,33 @@ public abstract class AbstractJPATest extends BaseCoreFunctionalTestCase {
super.applyServices( serviceRegistry ); super.applyServices( serviceRegistry );
serviceRegistry.getService( IntegratorService.class ).addIntegrator( serviceRegistry.getService( IntegratorService.class ).addIntegrator(
new Integrator() { new Integrator() {
@Override
@Override
public void integrate( public void integrate(
Configuration configuration, Configuration configuration,
SessionFactoryImplementor sessionFactory, SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) { SessionFactoryServiceRegistry serviceRegistry) {
EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); integrate(serviceRegistry);
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 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 @Override
public void disintegrate( public void disintegrate(
SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {

View File

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

View File

@ -35,13 +35,15 @@ import java.lang.annotation.Target;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XMethod; import org.hibernate.annotations.common.reflection.XMethod;
import org.hibernate.ejb.internal.EntityManagerMessageLogger; 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> * @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) { public static Callback[] resolveCallback(XClass beanClass, Class annotation, ReflectionManager reflectionManager) {
List<Callback> callbacks = new ArrayList<Callback>(); 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>(); List<Class> orderedListeners = new ArrayList<Class>();
XClass currentClazz = beanClass; XClass currentClazz = beanClass;
boolean stopListeners = false; boolean stopListeners = false;
@ -84,7 +86,7 @@ public final class CallbackResolver {
Method method = reflectionManager.toMethod( xMethod ); Method method = reflectionManager.toMethod( xMethod );
final String methodName = method.getName(); final String methodName = method.getName();
if ( ! callbacksMethodNames.contains( methodName ) ) { if ( ! callbacksMethodNames.contains( methodName ) ) {
//overriden method, remove the superclass overriden method //overridden method, remove the superclass overridden method
if ( callback == null ) { if ( callback == null ) {
callback = new BeanCallback( method ); callback = new BeanCallback( method );
Class returnType = method.getReturnType(); Class returnType = method.getReturnType();
@ -145,65 +147,117 @@ public final class CallbackResolver {
if ( listener != null ) { if ( listener != null ) {
XClass xListener = reflectionManager.toXClass( listener ); XClass xListener = reflectionManager.toXClass( listener );
callbacksMethodNames = new ArrayList<String>(); callbacksMethodNames = new ArrayList<String>();
do { List<XMethod> methods = xListener.getDeclaredMethods();
List<XMethod> methods = xListener.getDeclaredMethods(); final int size = methods.size();
final int size = methods.size(); for ( int i = 0; i < size ; i++ ) {
for ( int i = 0; i < size ; i++ ) { final XMethod xMethod = methods.get( i );
final XMethod xMethod = methods.get( i ); if ( xMethod.isAnnotationPresent( annotation ) ) {
if ( xMethod.isAnnotationPresent( annotation ) ) { final Method method = reflectionManager.toMethod( xMethod );
final Method method = reflectionManager.toMethod( xMethod ); final String methodName = method.getName();
final String methodName = method.getName(); if ( ! callbacksMethodNames.contains( methodName ) ) {
if ( ! callbacksMethodNames.contains( methodName ) ) { //overridden method, remove the superclass overridden method
//overriden method, remove the superclass overriden method if ( callback == null ) {
if ( callback == null ) { try {
try { callback = new ListenerCallback( method, listener.newInstance() );
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
} }
else { catch (IllegalAccessException e) {
throw new PersistenceException( throw new PersistenceException(
"You can only annotate one callback method with " "Unable to create instance of " + listener.getName()
+ annotation.getName() + " in bean class: " + beanClass.getName() + " and callback listener: " + " as a listener of beanClass", e
+ listener.getName()
); );
} }
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() ] ); 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) { private static void getListeners(XClass currentClazz, List<Class> orderedListeners) {
EntityListeners entityListeners = currentClazz.getAnnotation( EntityListeners.class ); EntityListeners entityListeners = currentClazz.getAnnotation( EntityListeners.class );
if ( entityListeners != null ) { 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.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass; 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 * 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 ); 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) { public boolean preCreate(Object bean) {
return callback( preCreates.get( bean.getClass() ), bean ); return callback( preCreates.get( bean.getClass() ), bean );
} }
@ -102,7 +116,6 @@ public class EntityCallbackHandler implements Serializable {
} }
} }
private void addCallback( private void addCallback(
XClass entity, HashMap<Class, Callback[]> map, Class annotation, ReflectionManager reflectionManager 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 ); callbacks = CallbackResolver.resolveCallback( entity, annotation, reflectionManager );
map.put( reflectionManager.toClass( entity ), callbacks ); 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.ejb.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.spi.EventType; import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreDeleteEventListener;
import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.Integrator;
import org.hibernate.mapping.PersistentClass; 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.JACCPreDeleteEventListener;
import org.hibernate.secure.internal.JACCPreInsertEventListener; import org.hibernate.secure.internal.JACCPreInsertEventListener;
import org.hibernate.secure.internal.JACCPreLoadEventListener; import org.hibernate.secure.internal.JACCPreLoadEventListener;
import org.hibernate.secure.internal.JACCPreUpdateEventListener; import org.hibernate.secure.internal.JACCPreUpdateEventListener;
import org.hibernate.secure.internal.JACCSecurityListener; import org.hibernate.secure.internal.JACCSecurityListener;
import org.hibernate.service.classloading.spi.ClassLoaderService; 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.DuplicationStrategy;
import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.service.spi.EventListenerRegistry;
@ -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 @Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
} }
@ -173,6 +258,6 @@ public class JpaIntegrator implements Integrator {
} }
catch (Exception e) { catch (Exception e) {
throw new HibernateException( "Could not instantiate requested listener [" + listenerImpl + "]", e ); throw new HibernateException( "Could not instantiate requested listener [" + listenerImpl + "]", e );
} }
} }
} }

View File

@ -32,6 +32,7 @@ import org.hibernate.cfg.Configuration;
import org.hibernate.envers.configuration.AuditConfiguration; import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.Integrator;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.service.spi.SessionFactoryServiceRegistry; import org.hibernate.service.spi.SessionFactoryServiceRegistry;
@ -75,4 +76,16 @@ public class EnversIntegrator implements Integrator {
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
// nothing to do afaik // 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
}
} }