Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HHH-14869 Extract JPA listener definitions when building metadata rather than when creating the SessionFactory #4296

Merged
merged 2 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.jpa.event.internal.CallbackDefinitionResolverLegacyImpl;
import org.hibernate.jpa.event.spi.CallbackType;
import org.hibernate.loader.PropertyPath;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Component;
Expand Down Expand Up @@ -837,6 +839,8 @@ else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Table.class ) );
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Tables.class ) );
entityBinder.processComplementaryTableDefinitions( tabAnn );

bindCallbacks( clazzToProcess, persistentClass, context );
}

/**
Expand Down Expand Up @@ -1420,6 +1424,32 @@ private static void bindTypeDef(TypeDef defAnn, MetadataBuildingContext context)

}

private static void bindCallbacks(XClass entityClass, PersistentClass persistentClass,
MetadataBuildingContext context) {
ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();

for ( CallbackType callbackType : CallbackType.values() ) {
persistentClass.addCallbackDefinitions( CallbackDefinitionResolverLegacyImpl.resolveEntityCallbacks(
reflectionManager, entityClass, callbackType ) );
}

context.getMetadataCollector().addSecondPass( new SecondPass() {
@Override
public void doSecondPass(Map persistentClasses) throws MappingException {
for ( @SuppressWarnings("unchecked") Iterator<Property> propertyIterator = persistentClass.getDeclaredPropertyIterator();
propertyIterator.hasNext(); ) {
Property property = propertyIterator.next();
if ( property.isComposite() ) {
for ( CallbackType callbackType : CallbackType.values() ) {
property.addCallbackDefinitions( CallbackDefinitionResolverLegacyImpl.resolveEmbeddableCallbacks(
reflectionManager, persistentClass.getMappedClass(), property, callbackType ) );
}
}
}
}
} );
}

public static void bindFetchProfilesForClass(XClass clazzToProcess, MetadataBuildingContext context) {
bindFetchProfiles( clazzToProcess, context );
}
Expand Down Expand Up @@ -1464,7 +1494,6 @@ private static void bindFetchProfile(FetchProfile fetchProfileAnnotation, Metada
}
}


private static void bindDiscriminatorColumnToRootPersistentClass(
RootClass rootClass,
Ejb3DiscriminatorColumn discriminatorColumn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Consumer;

Expand All @@ -23,9 +22,6 @@
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.event.internal.CallbackRegistryImplementor;
import org.hibernate.jpa.event.internal.CallbacksFactory;
import org.hibernate.jpa.event.spi.CallbackBuilder;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.service.spi.Stoppable;

/**
Expand All @@ -39,7 +35,6 @@ public class EventEngine {
private final EventListenerRegistry listenerRegistry;

private final CallbackRegistryImplementor callbackRegistry;
private final CallbackBuilder callbackBuilder;

public EventEngine(
MetadataImplementor mappings,
Expand All @@ -48,33 +43,8 @@ public EventEngine(
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// resolve (JPA) callback handlers

this.callbackRegistry = CallbacksFactory.buildCallbackRegistry( sessionFactory.getSessionFactoryOptions() );
this.callbackBuilder = CallbacksFactory.buildCallbackBuilder(
sessionFactory.getSessionFactoryOptions(),
sessionFactory.getServiceRegistry(),
mappings.getMetadataBuildingOptions().getReflectionManager()
);

for ( PersistentClass persistentClass : mappings.getEntityBindings() ) {
if ( persistentClass.getClassName() == null ) {
// we can have dynamic (non-java class) mapping
continue;
}

this.callbackBuilder.buildCallbacksForEntity( persistentClass.getMappedClass(), callbackRegistry );

for ( Iterator<Property> propertyIterator = persistentClass.getDeclaredPropertyIterator(); propertyIterator.hasNext(); ) {
final Property property = propertyIterator.next();

if ( property.isComposite() ) {
this.callbackBuilder.buildCallbacksForEmbeddable(
property,
persistentClass.getMappedClass(),
callbackRegistry
);
}
}
}
this.callbackRegistry = CallbacksFactory.buildCallbackRegistry( sessionFactory.getSessionFactoryOptions(),
sessionFactory.getServiceRegistry(), mappings.getEntityBindings() );


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -192,7 +162,5 @@ public void stop() {
}

callbackRegistry.release();

callbackBuilder.release();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,97 +19,48 @@
import javax.persistence.MappedSuperclass;
import javax.persistence.PersistenceException;

import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XMethod;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.jpa.event.spi.Callback;
import org.hibernate.jpa.event.spi.CallbackBuilder;
import org.hibernate.jpa.event.spi.CallbackDefinition;
import org.hibernate.jpa.event.spi.CallbackType;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;

import org.jboss.logging.Logger;

/**
* EntityCallbackBuilder implementation using HCANN ReflectionManager. "legacy" in that
* we want to move to Jandex instead.
* Resolves JPA callback definitions using a HCANN ReflectionManager.
* <p>
* "legacy" in that we want to move to Jandex instead.
*
* @author Steve Ebersole
*/
final class CallbackBuilderLegacyImpl implements CallbackBuilder {
private static final Logger log = Logger.getLogger( CallbackBuilderLegacyImpl.class );

private final ManagedBeanRegistry managedBeanRegistry;
private final ReflectionManager reflectionManager;

CallbackBuilderLegacyImpl(ManagedBeanRegistry managedBeanRegistry, ReflectionManager reflectionManager) {
this.managedBeanRegistry = managedBeanRegistry;
this.reflectionManager = reflectionManager;
}

@Override
public void buildCallbacksForEntity(Class entityClass, CallbackRegistrar callbackRegistrar) {
for ( CallbackType callbackType : CallbackType.values() ) {
if ( callbackRegistrar.hasRegisteredCallbacks( entityClass, callbackType ) ) {
// this most likely means we have a class mapped multiple times using the hbm.xml
// "entity name" feature
if ( log.isDebugEnabled() ) {
log.debugf(
"CallbackRegistry reported that Class [%s] already had %s callbacks registered; " +
"assuming this means the class was mapped twice " +
"(using hbm.xml entity-name support) - skipping subsequent registrations",
entityClass.getName(),
callbackType.getCallbackAnnotation().getSimpleName()
);
}
continue;
}
final Callback[] callbacks = resolveEntityCallbacks( entityClass, callbackType, reflectionManager );
callbackRegistrar.registerCallbacks( entityClass, callbacks );
}
}

@Override
public void buildCallbacksForEmbeddable(
Property embeddableProperty, Class entityClass, CallbackRegistrar callbackRegistrar) {
for ( CallbackType callbackType : CallbackType.values() ) {
final Callback[] callbacks = resolveEmbeddableCallbacks(
entityClass,
embeddableProperty,
callbackType,
reflectionManager
);
callbackRegistrar.registerCallbacks( entityClass, callbacks );
}
}

@Override
public void release() {
// nothing to do
}
public final class CallbackDefinitionResolverLegacyImpl {
private static final Logger log = Logger.getLogger( CallbackDefinitionResolverLegacyImpl.class );

@SuppressWarnings({"unchecked", "WeakerAccess"})
public Callback[] resolveEntityCallbacks(Class entityClass, CallbackType callbackType, ReflectionManager reflectionManager) {
List<Callback> callbacks = new ArrayList<>();
public static List<CallbackDefinition> resolveEntityCallbacks(ReflectionManager reflectionManager,
XClass entityClass, CallbackType callbackType) {
List<CallbackDefinition> callbackDefinitions = new ArrayList<>();
List<String> callbacksMethodNames = new ArrayList<>();
List<Class> orderedListeners = new ArrayList<>();
XClass currentClazz = reflectionManager.toXClass( entityClass );
XClass currentClazz = entityClass;
boolean stopListeners = false;
boolean stopDefaultListeners = false;
do {
Callback callback = null;
CallbackDefinition callbackDefinition = null;
List<XMethod> methods = currentClazz.getDeclaredMethods();
for ( final XMethod xMethod : methods ) {
if ( xMethod.isAnnotationPresent( callbackType.getCallbackAnnotation() ) ) {
Method method = reflectionManager.toMethod( xMethod );
final String methodName = method.getName();
if ( !callbacksMethodNames.contains( methodName ) ) {
//overridden method, remove the superclass overridden method
if ( callback == null ) {
callback = new EntityCallback( method, callbackType );
if ( callbackDefinition == null ) {
callbackDefinition = new EntityCallback.Definition( method, callbackType );
Class returnType = method.getReturnType();
Class[] args = method.getParameterTypes();
if ( returnType != Void.TYPE || args.length != 0 ) {
Expand All @@ -127,7 +78,7 @@ public Callback[] resolveEntityCallbacks(Class entityClass, CallbackType callbac
entityClass.getName()
);
}
callbacks.add( 0, callback ); //superclass first
callbackDefinitions.add( 0, callbackDefinition ); //superclass first
callbacksMethodNames.add( 0, methodName );
}
else {
Expand Down Expand Up @@ -168,7 +119,7 @@ public Callback[] resolveEntityCallbacks(Class entityClass, CallbackType callbac
}

for ( Class listener : orderedListeners ) {
Callback callback = null;
CallbackDefinition callbackDefinition = null;
if ( listener != null ) {
XClass xListener = reflectionManager.toXClass( listener );
callbacksMethodNames = new ArrayList<>();
Expand All @@ -179,12 +130,8 @@ public Callback[] resolveEntityCallbacks(Class entityClass, CallbackType callbac
final String methodName = method.getName();
if ( !callbacksMethodNames.contains( methodName ) ) {
//overridden method, remove the superclass overridden method
if ( callback == null ) {
callback = new ListenerCallback(
managedBeanRegistry.getBean( listener ),
method,
callbackType
);
if ( callbackDefinition == null ) {
callbackDefinition = new ListenerCallback.Definition( listener, method, callbackType );

Class returnType = method.getReturnType();
Class[] args = method.getParameterTypes();
Expand All @@ -203,7 +150,7 @@ public Callback[] resolveEntityCallbacks(Class entityClass, CallbackType callbac
entityClass.getName()
);
}
callbacks.add( 0, callback ); // listeners first
callbackDefinitions.add( 0, callbackDefinition ); // listeners first
}
else {
throw new PersistenceException(
Expand All @@ -218,29 +165,29 @@ public Callback[] resolveEntityCallbacks(Class entityClass, CallbackType callbac
}
}
}
return callbacks.toArray( new Callback[callbacks.size()] );
return callbackDefinitions;
}

@SuppressWarnings({"unchecked", "WeakerAccess"})
public Callback[] resolveEmbeddableCallbacks(Class entityClass, Property embeddableProperty, CallbackType callbackType, ReflectionManager reflectionManager) {

public static List<CallbackDefinition> resolveEmbeddableCallbacks(ReflectionManager reflectionManager,
Class<?> entityClass, Property embeddableProperty,
CallbackType callbackType) {
final Class embeddableClass = embeddableProperty.getType().getReturnedClass();
final XClass embeddableXClass = reflectionManager.toXClass( embeddableClass );
final Getter embeddableGetter = embeddableProperty.getGetter( entityClass );
final List<Callback> callbacks = new ArrayList<>();
final List<CallbackDefinition> callbackDefinitions = new ArrayList<>();
final List<String> callbacksMethodNames = new ArrayList<>();
XClass currentClazz = embeddableXClass;
do {
Callback callback = null;
CallbackDefinition callbackDefinition = null;
List<XMethod> methods = currentClazz.getDeclaredMethods();
for ( final XMethod xMethod : methods ) {
if ( xMethod.isAnnotationPresent( callbackType.getCallbackAnnotation() ) ) {
Method method = reflectionManager.toMethod( xMethod );
final String methodName = method.getName();
if ( !callbacksMethodNames.contains( methodName ) ) {
//overridden method, remove the superclass overridden method
if ( callback == null ) {
callback = new EmbeddableCallback( embeddableGetter, method, callbackType );
if ( callbackDefinition == null ) {
callbackDefinition = new EmbeddableCallback.Definition( embeddableGetter, method, callbackType );
Class returnType = method.getReturnType();
Class[] args = method.getParameterTypes();
if ( returnType != Void.TYPE || args.length != 0 ) {
Expand All @@ -258,7 +205,7 @@ public Callback[] resolveEmbeddableCallbacks(Class entityClass, Property embedda
embeddableXClass.getName()
);
}
callbacks.add( 0, callback ); //superclass first
callbackDefinitions.add( 0, callbackDefinition ); //superclass first
callbacksMethodNames.add( 0, methodName );
}
else {
Expand All @@ -278,7 +225,7 @@ public Callback[] resolveEmbeddableCallbacks(Class entityClass, Property embedda
}
while ( currentClazz != null );

return callbacks.toArray( new Callback[callbacks.size()] );
return callbackDefinitions;
}

private static boolean useAnnotationAnnotatedByListener;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ public void registerCallbacks(Class entityClass, Callback[] callbacks) {
return;
}

final HashMap<Class, Callback[]> map = determineAppropriateCallbackMap( callbacks[0].getCallbackType() );
Callback[] entityCallbacks = map.get( entityClass );

if ( entityCallbacks != null ) {
callbacks = ArrayHelper.join( entityCallbacks, callbacks );
for ( Callback callback : callbacks ) {
final HashMap<Class, Callback[]> map = determineAppropriateCallbackMap( callback.getCallbackType() );
Callback[] entityCallbacks = map.get( entityClass );
if ( entityCallbacks == null ) {
entityCallbacks = new Callback[0];
}
entityCallbacks = ArrayHelper.join( entityCallbacks, callback );
map.put( entityClass, entityCallbacks );
}
map.put( entityClass, callbacks );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
package org.hibernate.jpa.event.internal;

import org.hibernate.jpa.event.spi.CallbackBuilder;
import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.jpa.event.spi.CallbackRegistrar;

public interface CallbackRegistryImplementor extends CallbackRegistry, CallbackBuilder.CallbackRegistrar {
public interface CallbackRegistryImplementor extends CallbackRegistrar, CallbackBuilder.CallbackRegistrar {

void release();

Expand Down
Loading