HHH-17460 - Ongoing JPA 32 work
This commit is contained in:
parent
9b46ced2b3
commit
808544579c
|
@ -63,7 +63,6 @@ import org.hibernate.annotations.Tables;
|
|||
import org.hibernate.annotations.TypeBinderType;
|
||||
import org.hibernate.annotations.View;
|
||||
import org.hibernate.annotations.Where;
|
||||
import org.hibernate.annotations.common.reflection.ReflectionManager;
|
||||
import org.hibernate.binder.TypeBinder;
|
||||
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
|
||||
import org.hibernate.boot.model.NamedEntityGraphDefinition;
|
||||
|
@ -74,6 +73,7 @@ import org.hibernate.boot.model.naming.ImplicitEntityNameSource;
|
|||
import org.hibernate.boot.model.naming.NamingStrategyHelper;
|
||||
import org.hibernate.boot.model.relational.QualifiedTableName;
|
||||
import org.hibernate.boot.models.HibernateAnnotations;
|
||||
import org.hibernate.boot.models.categorize.spi.JpaEventListener;
|
||||
import org.hibernate.boot.spi.AccessType;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
|
@ -85,6 +85,7 @@ import org.hibernate.internal.CoreMessageLogger;
|
|||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.jpa.event.internal.CallbackDefinitionResolver;
|
||||
import org.hibernate.jpa.event.spi.CallbackType;
|
||||
import org.hibernate.mapping.BasicValue;
|
||||
import org.hibernate.mapping.CheckConstraint;
|
||||
|
@ -166,8 +167,6 @@ import static org.hibernate.internal.util.StringHelper.isEmpty;
|
|||
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.unqualify;
|
||||
import static org.hibernate.jpa.event.internal.CallbackDefinitionResolverLegacyImpl.resolveEmbeddableCallbacks;
|
||||
import static org.hibernate.jpa.event.internal.CallbackDefinitionResolverLegacyImpl.resolveEntityCallbacks;
|
||||
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
|
||||
|
||||
|
||||
|
@ -655,7 +654,7 @@ public class EntityBinder {
|
|||
propertyAccessor,
|
||||
context
|
||||
);
|
||||
final InheritanceState state = inheritanceStates.get( idPropertyOnBaseClass.getClassOrElementType() );
|
||||
final InheritanceState state = inheritanceStates.get( idPropertyOnBaseClass.getClassOrElementType().determineRawClass() );
|
||||
if ( state == null ) {
|
||||
return false; //while it is likely a user error, let's consider it is something that might happen
|
||||
}
|
||||
|
@ -687,11 +686,10 @@ public class EntityBinder {
|
|||
|
||||
// Fill simple value and property since and Id is a property
|
||||
final PersistentClass persistentClass = propertyHolder.getPersistentClass();
|
||||
if ( !(persistentClass instanceof RootClass) ) {
|
||||
if ( !( persistentClass instanceof RootClass rootClass ) ) {
|
||||
throw new AnnotationException( "Entity '" + persistentClass.getEntityName()
|
||||
+ "' is a subclass in an entity inheritance hierarchy and may not redefine the identifier of the root entity" );
|
||||
}
|
||||
final RootClass rootClass = (RootClass) persistentClass;
|
||||
final Component id = fillEmbeddable(
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
|
@ -1225,17 +1223,29 @@ public class EntityBinder {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link JpaEventListener} for a better (?) alternative
|
||||
*/
|
||||
private static void bindCallbacks(ClassDetails entityClass, PersistentClass persistentClass, MetadataBuildingContext context) {
|
||||
final ReflectionManager reflection = context.getBootstrapContext().getReflectionManager();
|
||||
for ( CallbackType callbackType : CallbackType.values() ) {
|
||||
persistentClass.addCallbackDefinitions( resolveEntityCallbacks( reflection, entityClass, callbackType ) );
|
||||
persistentClass.addCallbackDefinitions( CallbackDefinitionResolver.resolveEntityCallbacks(
|
||||
context,
|
||||
entityClass,
|
||||
callbackType
|
||||
) );
|
||||
}
|
||||
|
||||
context.getMetadataCollector().addSecondPass( persistentClasses -> {
|
||||
for ( Property property : persistentClass.getDeclaredProperties() ) {
|
||||
final Class<?> mappedClass = persistentClass.getMappedClass();
|
||||
if ( property.isComposite() ) {
|
||||
for ( CallbackType type : CallbackType.values() ) {
|
||||
property.addCallbackDefinitions( resolveEmbeddableCallbacks( reflection, mappedClass, property, type ) );
|
||||
property.addCallbackDefinitions( CallbackDefinitionResolver.resolveEmbeddableCallbacks(
|
||||
context,
|
||||
mappedClass,
|
||||
property,
|
||||
type
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2101,39 +2111,6 @@ public class EntityBinder {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static <T extends Annotation> String tableMember(Class<T> annotationType, T sqlAnnotation) {
|
||||
if (SQLInsert.class == annotationType) {
|
||||
return ((SQLInsert) sqlAnnotation).table();
|
||||
}
|
||||
else if (SQLUpdate.class == annotationType) {
|
||||
return ((SQLUpdate) sqlAnnotation).table();
|
||||
}
|
||||
else if (SQLDelete.class == annotationType) {
|
||||
return ((SQLDelete) sqlAnnotation).table();
|
||||
}
|
||||
else if (SQLDeleteAll.class == annotationType) {
|
||||
return ((SQLDeleteAll) sqlAnnotation).table();
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure("Unknown annotation type");
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Annotation> Annotation[] valueMember(Class<T> repeatableType, T sqlAnnotation) {
|
||||
if (SQLInserts.class == repeatableType) {
|
||||
return ((SQLInserts) sqlAnnotation).value();
|
||||
}
|
||||
else if (SQLUpdates.class == repeatableType) {
|
||||
return ((SQLUpdates) sqlAnnotation).value();
|
||||
}
|
||||
else if (SQLDeletes.class == repeatableType) {
|
||||
return ((SQLDeletes) sqlAnnotation).value();
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure("Unknown annotation type");
|
||||
}
|
||||
}
|
||||
|
||||
//Used for @*ToMany @JoinTable
|
||||
public Join addJoinTable(AnnotationUsage<JoinTable> joinTable, PropertyHolder holder, boolean noDelayInPkColumnCreation) {
|
||||
return addJoin(
|
||||
|
|
|
@ -164,8 +164,11 @@ public interface BootstrapContext {
|
|||
* @apiNote Supported for internal use only. This method will go away as
|
||||
* we migrate away from Hibernate Commons Annotations to Jandex for
|
||||
* annotation handling and XMl to annotation merging.
|
||||
*
|
||||
* @deprecated HCANN is deprecated in favor of hibernate-models
|
||||
*/
|
||||
@Internal
|
||||
@Deprecated
|
||||
ReflectionManager getReflectionManager();
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,18 +38,12 @@ public enum OptimisticLockStyle {
|
|||
ALL;
|
||||
|
||||
public static OptimisticLockStyle fromLockType(OptimisticLockType type) {
|
||||
switch ( type ) {
|
||||
case VERSION:
|
||||
return VERSION;
|
||||
case NONE:
|
||||
return NONE;
|
||||
case DIRTY:
|
||||
return DIRTY;
|
||||
case ALL:
|
||||
return ALL;
|
||||
default:
|
||||
throw new AssertionFailure( "Unrecognized OptimisticLockType" );
|
||||
}
|
||||
return switch ( type ) {
|
||||
case VERSION -> VERSION;
|
||||
case NONE -> NONE;
|
||||
case DIRTY -> DIRTY;
|
||||
case ALL -> ALL;
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isAllOrDirty() {
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.jpa.event.internal;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
|
||||
import org.hibernate.boot.models.categorize.spi.JpaEventListener;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.jpa.event.spi.CallbackDefinition;
|
||||
import org.hibernate.jpa.event.spi.CallbackType;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.models.spi.AnnotationUsage;
|
||||
import org.hibernate.models.spi.ClassDetails;
|
||||
import org.hibernate.models.spi.ClassDetailsRegistry;
|
||||
import org.hibernate.models.spi.MethodDetails;
|
||||
import org.hibernate.models.spi.SourceModelBuildingContext;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityListeners;
|
||||
import jakarta.persistence.ExcludeDefaultListeners;
|
||||
import jakarta.persistence.ExcludeSuperclassListeners;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Resolves JPA callback definitions
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public final class CallbackDefinitionResolver {
|
||||
private static final Logger log = Logger.getLogger( CallbackDefinitionResolver.class );
|
||||
|
||||
public static List<CallbackDefinition> resolveEntityCallbacks(
|
||||
MetadataBuildingContext metadataBuildingContext,
|
||||
ClassDetails entityClass,
|
||||
CallbackType callbackType) {
|
||||
final GlobalRegistrations globalRegistrations = metadataBuildingContext.getMetadataCollector().getGlobalRegistrations();
|
||||
final List<JpaEventListener> globalListenerRegistrations = globalRegistrations.getEntityListenerRegistrations();
|
||||
|
||||
List<CallbackDefinition> callbackDefinitions = new ArrayList<>();
|
||||
List<String> callbacksMethodNames = new ArrayList<>();
|
||||
List<ClassDetails> orderedListeners = new ArrayList<>();
|
||||
|
||||
ClassDetails currentClazz = entityClass;
|
||||
boolean stopListeners = false;
|
||||
boolean stopDefaultListeners = false;
|
||||
|
||||
do {
|
||||
CallbackDefinition callbackDefinition = null;
|
||||
final List<MethodDetails> methodsDetailsList = currentClazz.getMethods();
|
||||
for ( MethodDetails methodDetails : methodsDetailsList ) {
|
||||
if ( !methodDetails.hasAnnotationUsage( callbackType.getCallbackAnnotation() ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( callbacksMethodNames.contains( methodDetails.getName() ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//overridden method, remove the superclass overridden method
|
||||
if ( callbackDefinition == null ) {
|
||||
final Method javaMethod = (Method) methodDetails.toJavaMember();
|
||||
callbackDefinition = new EntityCallback.Definition( javaMethod, callbackType );
|
||||
Class<?> returnType = javaMethod.getReturnType();
|
||||
Class<?>[] args = javaMethod.getParameterTypes();
|
||||
if ( returnType != Void.TYPE || args.length != 0 ) {
|
||||
throw new RuntimeException(
|
||||
"Callback methods annotated on the bean class must return void and take no arguments: "
|
||||
+ callbackType.getCallbackAnnotation().getName() + " - " + methodDetails
|
||||
);
|
||||
}
|
||||
ReflectHelper.ensureAccessibility( javaMethod );
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf(
|
||||
"Adding %s as %s callback for entity %s",
|
||||
methodDetails.getName(),
|
||||
callbackType.getCallbackAnnotation().getSimpleName(),
|
||||
entityClass.getName()
|
||||
);
|
||||
}
|
||||
callbackDefinitions.add( 0, callbackDefinition ); //superclass first
|
||||
callbacksMethodNames.add( 0, methodDetails.getName() );
|
||||
}
|
||||
else {
|
||||
throw new PersistenceException(
|
||||
"You can only annotate one callback method with "
|
||||
+ callbackType.getCallbackAnnotation().getName() + " in bean class: " + entityClass.getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
if ( !stopListeners ) {
|
||||
applyListeners( currentClazz, orderedListeners );
|
||||
stopListeners = currentClazz.hasAnnotationUsage( ExcludeSuperclassListeners.class );
|
||||
stopDefaultListeners = currentClazz.hasAnnotationUsage( ExcludeDefaultListeners.class );
|
||||
}
|
||||
|
||||
do {
|
||||
currentClazz = currentClazz.getSuperClass();
|
||||
}
|
||||
while ( currentClazz != null
|
||||
&& !( currentClazz.hasAnnotationUsage( Entity.class )
|
||||
|| currentClazz.hasAnnotationUsage( MappedSuperclass.class ) )
|
||||
);
|
||||
}
|
||||
while ( currentClazz != null );
|
||||
|
||||
//handle default listeners
|
||||
if ( !stopDefaultListeners ) {
|
||||
if ( CollectionHelper.isNotEmpty( globalListenerRegistrations ) ) {
|
||||
int defaultListenerSize = globalListenerRegistrations.size();
|
||||
for ( int i = defaultListenerSize - 1; i >= 0; i-- ) {
|
||||
orderedListeners.add( globalListenerRegistrations.get( i ).getCallbackClass() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( ClassDetails listenerClassDetails : orderedListeners ) {
|
||||
CallbackDefinition callbackDefinition = null;
|
||||
if ( listenerClassDetails != null ) {
|
||||
for ( MethodDetails methodDetails : listenerClassDetails.getMethods() ) {
|
||||
if ( methodDetails.hasAnnotationUsage( callbackType.getCallbackAnnotation() ) ) {
|
||||
final String methodName = methodDetails.getName();
|
||||
//overridden method, remove the superclass overridden method
|
||||
if ( callbackDefinition == null ) {
|
||||
final Method method = (Method) methodDetails.toJavaMember();
|
||||
callbackDefinition = new ListenerCallback.Definition(
|
||||
listenerClassDetails.toJavaClass(),
|
||||
method,
|
||||
callbackType
|
||||
);
|
||||
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
final 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: "
|
||||
+ callbackType.getCallbackAnnotation().getName() + " - " + methodDetails
|
||||
);
|
||||
}
|
||||
ReflectHelper.ensureAccessibility( method );
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf(
|
||||
"Adding %s as %s callback for entity %s",
|
||||
methodName,
|
||||
callbackType.getCallbackAnnotation().getSimpleName(),
|
||||
entityClass.getName()
|
||||
);
|
||||
}
|
||||
callbackDefinitions.add( 0, callbackDefinition ); // listeners first
|
||||
}
|
||||
else {
|
||||
throw new PersistenceException(
|
||||
"You can only annotate one callback method with "
|
||||
+ callbackType.getCallbackAnnotation().getName()
|
||||
+ " in bean class: " + entityClass.getName()
|
||||
+ " and callback listener: " + listenerClassDetails.getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return callbackDefinitions;
|
||||
}
|
||||
|
||||
public static List<CallbackDefinition> resolveEmbeddableCallbacks(
|
||||
MetadataBuildingContext metadataBuildingContext,
|
||||
Class<?> entityClass,
|
||||
Property embeddableProperty,
|
||||
CallbackType callbackType) {
|
||||
final SourceModelBuildingContext hibernateModelsContext = metadataBuildingContext.getMetadataCollector().getSourceModelBuildingContext();
|
||||
final ClassDetailsRegistry classDetailsRegistry = hibernateModelsContext.getClassDetailsRegistry();
|
||||
|
||||
final Class<?> embeddableClass = embeddableProperty.getType().getReturnedClass();
|
||||
final ClassDetails embeddableClassDetails = classDetailsRegistry.getClassDetails( embeddableClass.getName() );
|
||||
|
||||
final Getter embeddableGetter = embeddableProperty.getGetter( entityClass );
|
||||
final List<CallbackDefinition> callbackDefinitions = new ArrayList<>();
|
||||
final List<String> callbacksMethodNames = new ArrayList<>();
|
||||
ClassDetails currentClazz = embeddableClassDetails;
|
||||
do {
|
||||
CallbackDefinition callbackDefinition = null;
|
||||
final List<MethodDetails> methodsDetailsList = currentClazz.getMethods();
|
||||
for ( MethodDetails methodDetails : methodsDetailsList ) {
|
||||
if ( !methodDetails.hasAnnotationUsage( callbackType.getCallbackAnnotation() ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Method method = (Method) methodDetails.toJavaMember();
|
||||
final String methodName = method.getName();
|
||||
|
||||
if ( callbacksMethodNames.contains( methodName ) ) {
|
||||
throw new PersistenceException(
|
||||
"You can only annotate one callback method with "
|
||||
+ callbackType.getCallbackAnnotation().getName() + " in bean class: " + currentClazz.getName()
|
||||
);
|
||||
}
|
||||
|
||||
//overridden method, remove the superclass overridden method
|
||||
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 ) {
|
||||
throw new RuntimeException(
|
||||
"Callback methods annotated on the bean class must return void and take no arguments: "
|
||||
+ callbackType.getCallbackAnnotation().getName() + " - " + methodDetails
|
||||
);
|
||||
}
|
||||
ReflectHelper.ensureAccessibility( method );
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debugf(
|
||||
"Adding %s as %s callback for entity %s",
|
||||
methodName,
|
||||
callbackType.getCallbackAnnotation().getSimpleName(),
|
||||
currentClazz.getName()
|
||||
);
|
||||
}
|
||||
callbackDefinitions.add( 0, callbackDefinition ); //superclass first
|
||||
callbacksMethodNames.add( 0, methodName );
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
currentClazz = currentClazz.getSuperClass();
|
||||
}
|
||||
while ( currentClazz != null && !currentClazz.hasAnnotationUsage( MappedSuperclass.class ) );
|
||||
}
|
||||
while ( currentClazz != null );
|
||||
|
||||
return callbackDefinitions;
|
||||
}
|
||||
|
||||
private static boolean useAnnotationAnnotatedByListener;
|
||||
|
||||
static {
|
||||
//check whether reading annotations of annotations is useful or not
|
||||
useAnnotationAnnotatedByListener = false;
|
||||
Target target = EntityListeners.class.getAnnotation( Target.class );
|
||||
if ( target != null ) {
|
||||
for ( ElementType type : target.value() ) {
|
||||
if ( type.equals( ElementType.ANNOTATION_TYPE ) ) {
|
||||
useAnnotationAnnotatedByListener = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyListeners(ClassDetails currentClazz, List<ClassDetails> listOfListeners) {
|
||||
final AnnotationUsage<EntityListeners> entityListeners = currentClazz.getAnnotationUsage( EntityListeners.class );
|
||||
if ( entityListeners != null ) {
|
||||
final List<ClassDetails> listeners = entityListeners.getList( "value" );
|
||||
listOfListeners.addAll( listeners );
|
||||
}
|
||||
|
||||
if ( useAnnotationAnnotatedByListener ) {
|
||||
final List<AnnotationUsage<?>> metaAnnotatedUsageList = currentClazz.getMetaAnnotated( EntityListeners.class );
|
||||
for ( AnnotationUsage<?> metaAnnotatedUsage : metaAnnotatedUsageList ) {
|
||||
final AnnotationUsage<EntityListeners> metaAnnotatedListeners = metaAnnotatedUsage.getAnnotationDescriptor().getAnnotationUsage( EntityListeners.class );
|
||||
final List<ClassDetails> listeners = metaAnnotatedListeners.getList( "value" );
|
||||
listOfListeners.addAll( listeners );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue