diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java index 3f55041ba5..861132369f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java @@ -43,7 +43,7 @@ import org.hibernate.models.spi.MemberDetails; import org.jboss.logging.Logger; -import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation; +import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.getPath; import static org.hibernate.boot.model.internal.BinderHelper.getRelativePath; import static org.hibernate.internal.util.StringHelper.isEmpty; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnyBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnyBinder.java index 0cc8f3ef38..f125086f48 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnyBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnyBinder.java @@ -28,7 +28,7 @@ import jakarta.persistence.FetchType; import jakarta.persistence.JoinTable; import static org.hibernate.boot.model.internal.BinderHelper.getCascadeStrategy; -import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation; +import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.getPath; public class AnyBinder { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java index 10565ee0a9..8de75c33be 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BinderHelper.java @@ -21,7 +21,6 @@ import java.util.function.Consumer; import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; import org.hibernate.FetchMode; -import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.annotations.AnyDiscriminatorValue; import org.hibernate.annotations.AnyDiscriminatorValues; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java index 3e545e8db3..0fd74ac0a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java @@ -166,7 +166,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.createSyntheticProp import static org.hibernate.boot.model.internal.BinderHelper.extractFromPackage; import static org.hibernate.boot.model.internal.BinderHelper.getCascadeStrategy; import static org.hibernate.boot.model.internal.BinderHelper.getFetchMode; -import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation; +import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.getPath; import static org.hibernate.boot.model.internal.BinderHelper.isDefault; import static org.hibernate.boot.model.internal.BinderHelper.isPrimitive; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java index 87ad39a713..9ad20c8aef 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ColumnsBuilder.java @@ -44,7 +44,7 @@ import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnFromA import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnFromNoAnnotation; import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnsFromAnnotations; import static org.hibernate.boot.model.internal.AnnotatedColumn.buildFormulaFromAnnotation; -import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation; +import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.getPath; import static org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId; import static org.hibernate.boot.models.internal.AnnotationUsageHelper.applyAttributeIfSpecified; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/DialectOverridesAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/DialectOverridesAnnotationHelper.java new file mode 100644 index 0000000000..ace9093749 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/DialectOverridesAnnotationHelper.java @@ -0,0 +1,119 @@ +/* + * 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.boot.model.internal; + +import java.lang.annotation.Annotation; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.hibernate.HibernateException; +import org.hibernate.annotations.DialectOverride; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; +import org.hibernate.models.spi.AnnotationTarget; +import org.hibernate.models.spi.AnnotationUsage; +import org.hibernate.models.spi.ClassDetails; + +/** + * @author Steve Ebersole + */ +public class DialectOverridesAnnotationHelper { + private static Map, Class> OVERRIDE_MAP = buildOverrideMap(); + + private static Map, Class> buildOverrideMap() { + // not accessed concurrently + final Map, Class> results = new HashMap<>(); + + final Class[] dialectOverrideMembers = DialectOverride.class.getNestMembers(); + for ( Class dialectOverrideMember : dialectOverrideMembers ) { + if ( !dialectOverrideMember.isAnnotation() ) { + continue; + } + + final DialectOverride.OverridesAnnotation overrideAnnotation = dialectOverrideMember.getAnnotation( DialectOverride.OverridesAnnotation.class ); + if ( overrideAnnotation == null ) { + continue; + } + + // The "real" annotation. e.g. `org.hibernate.annotations.Formula` + final Class baseAnnotation = overrideAnnotation.value(); + + // the "override" annotation. e.g. `org.hibernate.annotations.DialectOverride.Formula` + //noinspection unchecked + final Class dialectOverrideAnnotation = (Class) dialectOverrideMember; + + results.put( baseAnnotation, dialectOverrideAnnotation ); + } + + return results; + } + + public static Class getOverrideAnnotation(Class annotationType) { + final Class overrideAnnotation = findOverrideAnnotation( annotationType ); + if ( overrideAnnotation != null ) { + return overrideAnnotation; + } + throw new HibernateException( + String.format( + Locale.ROOT, + "Specified Annotation type (%s) does not have an override form", + annotationType.getName() + ) + ); + } + + public static Class findOverrideAnnotation(Class annotationType) { + //noinspection unchecked + return (Class) OVERRIDE_MAP.get( annotationType ); + } + + public static AnnotationUsage getOverridableAnnotation( + AnnotationTarget element, + Class annotationType, + MetadataBuildingContext context) { + final Class overrideAnnotation = OVERRIDE_MAP.get( annotationType ); + if ( overrideAnnotation != null ) { + // the requested annotation does have a DialectOverride variant - look for matching one of those... + final Dialect dialect = context.getMetadataCollector().getDatabase().getDialect(); + final DatabaseVersion version = dialect.getVersion(); + + final List> overrides = element.getRepeatedAnnotationUsages( overrideAnnotation ); + for ( AnnotationUsage override : overrides ) { + if ( overrideMatchesDialect( override, dialect ) ) { + // we found an override match... + // the override's `override` attribute is the thing to return + return override.getNestedUsage( "override" ); + } + } + } + + // no override was found. return the base annotation (if one) + return element.getSingleAnnotationUsage( annotationType ); + } + + public static boolean overrideMatchesDialect(AnnotationUsage override, Dialect dialect) { + final ClassDetails overrideDialect = override.getClassDetails( "dialect" ); + final Class overrideDialectJavaType = overrideDialect.toJavaClass(); + if ( !overrideDialectJavaType.isAssignableFrom( dialect.getClass() ) ) { + return false; + } + + final AnnotationUsage beforeAnn = override.getNestedUsage( "before" ); + final AnnotationUsage sameOrAfterAnn = override.getNestedUsage( "sameOrAfter" ); + final DatabaseVersion version = dialect.getVersion(); + + if ( version.isBefore( beforeAnn.getInteger( "major" ), beforeAnn.getInteger( "minor" ) ) + && version.isSameOrAfter( sameOrAfterAnn.getInteger( "major" ), sameOrAfterAnn.getInteger( "minor" ) ) ) { + return true; + } + + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index 8c9ed66f8f..4731ad83b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -144,13 +144,13 @@ import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.bui import static org.hibernate.boot.model.internal.AnnotatedJoinColumn.buildInheritanceJoinColumn; import static org.hibernate.boot.model.internal.BinderHelper.extractFromPackage; import static org.hibernate.boot.model.internal.BinderHelper.getMappedSuperclassOrNull; -import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; -import static org.hibernate.boot.model.internal.BinderHelper.getOverrideAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.noConstraint; -import static org.hibernate.boot.model.internal.BinderHelper.overrideMatchesDialect; import static org.hibernate.boot.model.internal.BinderHelper.toAliasEntityMap; import static org.hibernate.boot.model.internal.BinderHelper.toAliasTableMap; +import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; +import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverrideAnnotation; +import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.overrideMatchesDialect; import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable; import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator; import static org.hibernate.boot.model.internal.InheritanceState.getInheritanceStateOfSuperEntity; @@ -1368,7 +1368,7 @@ public class EntityBinder { persistentClass.setCached( isCached ); persistentClass.setLazy( lazy ); persistentClass.setQueryCacheLayout( queryCacheLayout ); - if ( proxyClass != null ) { + if ( proxyClass != null && proxyClass != ClassDetails.VOID_CLASS_DETAILS ) { persistentClass.setProxyInterfaceName( proxyClass.getName() ); } @@ -1641,7 +1641,7 @@ public class EntityBinder { final AnnotationUsage proxy = annotatedClass.getAnnotationUsage( Proxy.class ); if ( proxy != null ) { lazy = proxy.getBoolean( "lazy" ); - proxyClass = lazy ? proxy.getClassDetails( "proxyClass" ) : null; + proxyClass = lazy ? resolveProxyClass( proxy, annotatedClass ) : null; } else { //needed to allow association lazy loading. @@ -1650,6 +1650,14 @@ public class EntityBinder { } } + private static ClassDetails resolveProxyClass(AnnotationUsage proxy, ClassDetails annotatedClass) { + final ClassDetails proxyClass = proxy.getClassDetails( "proxyClass" ); + if ( proxyClass == ClassDetails.VOID_CLASS_DETAILS ) { + return annotatedClass; + } + return proxyClass; + } + public void bindConcreteProxy() { final AnnotationUsage annotationUsage = annotatedClass.getAnnotationUsage( ConcreteProxy.class ); if ( annotationUsage != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java index af9530effe..1875422b3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java @@ -6,17 +6,6 @@ */ package org.hibernate.bytecode.internal.bytebuddy; -import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; -import static net.bytebuddy.matcher.ElementMatchers.isFinalizer; -import static net.bytebuddy.matcher.ElementMatchers.isSynthetic; -import static net.bytebuddy.matcher.ElementMatchers.isVirtual; -import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.returns; -import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; -import static org.hibernate.internal.CoreLogging.messageLogger; - import java.io.File; import java.io.IOException; import java.lang.invoke.MethodHandles; @@ -48,6 +37,17 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.pool.TypePool; +import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; +import static net.bytebuddy.matcher.ElementMatchers.isFinalizer; +import static net.bytebuddy.matcher.ElementMatchers.isSynthetic; +import static net.bytebuddy.matcher.ElementMatchers.isVirtual; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; +import static org.hibernate.internal.CoreLogging.messageLogger; + /** * A utility to hold all ByteBuddy related state, as in the current version of * Hibernate the Bytecode Provider state is held in a static field, yet ByteBuddy @@ -86,6 +86,7 @@ public final class ByteBuddyState { this.byteBuddy = new ByteBuddy( classFileVersion ).with( TypeValidation.DISABLED ); this.proxyCache = new TypeCache( TypeCache.Sort.WEAK ); this.basicProxyCache = new TypeCache( TypeCache.Sort.WEAK ); + this.classRewriter = new StandardClassRewriter(); } /** @@ -232,30 +233,6 @@ public final class ByteBuddyState { return this.enhancerConstants; } - private static class GetDeclaredMethodAction implements PrivilegedAction { - private final Class clazz; - private final String methodName; - private final Class[] parameterTypes; - - private GetDeclaredMethodAction(Class clazz, String methodName, Class... parameterTypes) { - this.clazz = clazz; - this.methodName = methodName; - this.parameterTypes = parameterTypes; - } - - @Override - public Method run() { - try { - Method method = clazz.getDeclaredMethod( methodName, parameterTypes ); - - return method; - } - catch (NoSuchMethodException e) { - throw new HibernateException( "Unable to prepare getDeclaredMethod()/getMethod() substitution", e ); - } - } - } - /** * Shared proxy definition helpers. They are immutable so we can safely share them. */ @@ -320,6 +297,25 @@ public final class ByteBuddyState { } } + private interface ClassRewriter { + DynamicType.Builder installReflectionMethodVisitors(DynamicType.Builder builder); + + void registerAuthorizedClass(Unloaded unloadedClass); + } + + private static class StandardClassRewriter implements ClassRewriter { + @Override + public DynamicType.Builder installReflectionMethodVisitors(DynamicType.Builder builder) { + // do nothing + return builder; + } + + @Override + public void registerAuthorizedClass(Unloaded unloadedClass) { + // do nothing + } + } + private static ClassLoadingStrategy resolveClassLoadingStrategy(Class originalClass) { try { return ClassLoadingStrategy.UsingLookup.of( MethodHandles.privateLookupIn( originalClass, LOOKUP ) );