HHH-18649 populate EntityGraph in static metamodel

This commit is contained in:
Gavin King 2024-09-22 17:30:20 +02:00
parent c863838e72
commit 600288d1bb
15 changed files with 460 additions and 338 deletions

View File

@ -42,7 +42,8 @@ public class NamedHqlQueryDefinitionImpl<E> extends AbstractNamedQueryDefinition
Integer fetchSize,
String comment,
Map<String,String> parameterTypes,
Map<String,Object> hints) {
Map<String,Object> hints,
String location) {
super(
name,
resultType,
@ -55,7 +56,8 @@ public class NamedHqlQueryDefinitionImpl<E> extends AbstractNamedQueryDefinition
timeout,
fetchSize,
comment,
hints
hints,
location
);
this.hqlString = hqlString;
this.firstResult = firstResult;

View File

@ -44,7 +44,8 @@ public class NamedNativeQueryDefinitionImpl<E> extends AbstractNamedQueryDefinit
String comment,
Integer firstResult,
Integer maxResults,
Map<String,Object> hints) {
Map<String,Object> hints,
String location) {
super(
name,
resultType,
@ -57,7 +58,8 @@ public class NamedNativeQueryDefinitionImpl<E> extends AbstractNamedQueryDefinit
timeout,
fetchSize,
comment,
hints
hints,
location
);
this.sqlString = sqlString;
this.resultSetMappingName = resultSetMappingName;

View File

@ -10,6 +10,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.MappingException;
@ -74,6 +75,12 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
return registeredName;
}
@Override
public @Nullable String getLocation() {
// not kept for now
return null;
}
@Override
public String getProcedureName() {
return procedureName;

View File

@ -104,11 +104,11 @@ public final class AnnotationBinder {
// queries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
globalRegistrations.getNamedQueryRegistrations().forEach( (name, queryRegistration) -> {
QueryBinder.bindQuery( queryRegistration.configuration(), context, true );
QueryBinder.bindQuery( queryRegistration.configuration(), context, true, null );
} );
globalRegistrations.getNamedNativeQueryRegistrations().forEach( (name, queryRegistration) -> {
QueryBinder.bindNativeQuery( queryRegistration.configuration(), context, true );
QueryBinder.bindNativeQuery( queryRegistration.configuration(), context, null, true );
} );
globalRegistrations.getNamedStoredProcedureQueryRegistrations().forEach( (name, queryRegistration) -> {
@ -152,13 +152,13 @@ public final class AnnotationBinder {
annotationTarget.forEachRepeatedAnnotationUsages(
HibernateAnnotations.NAMED_QUERY,
sourceModelContext,
(usage) -> QueryBinder.bindQuery( usage, context )
(usage) -> QueryBinder.bindQuery( usage, context, annotationTarget )
);
annotationTarget.forEachRepeatedAnnotationUsages(
HibernateAnnotations.NAMED_NATIVE_QUERY,
sourceModelContext,
(usage) -> QueryBinder.bindNativeQuery( usage, context )
(usage) -> QueryBinder.bindNativeQuery( usage, context, annotationTarget )
);
}
@ -174,13 +174,13 @@ public final class AnnotationBinder {
annotationTarget.forEachRepeatedAnnotationUsages(
JpaAnnotations.NAMED_QUERY,
sourceModelContext,
(usage) -> QueryBinder.bindQuery( usage, context, false )
(usage) -> QueryBinder.bindQuery( usage, context, false, annotationTarget )
);
annotationTarget.forEachRepeatedAnnotationUsages(
JpaAnnotations.NAMED_NATIVE_QUERY,
sourceModelContext,
(usage) -> QueryBinder.bindNativeQuery( usage, context, false )
(usage) -> QueryBinder.bindNativeQuery( usage, context, annotationTarget, false )
);
annotationTarget.forEachRepeatedAnnotationUsages(

View File

@ -14,6 +14,7 @@ import java.util.function.Supplier;
import org.hibernate.AnnotationException;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.query.QueryFlushMode;
import org.hibernate.LockOptions;
import org.hibernate.annotations.FlushModeType;
@ -74,7 +75,8 @@ public abstract class QueryBinder {
public static void bindQuery(
NamedQuery namedQuery,
MetadataBuildingContext context,
boolean isDefault) {
boolean isDefault,
AnnotationTarget annotationTarget) {
if ( namedQuery == null ) {
return;
}
@ -94,7 +96,7 @@ public abstract class QueryBinder {
final QueryHintDefinition hints = new QueryHintDefinition( queryName, namedQuery.hints() );
final NamedHqlQueryDefinition<?> queryMapping =
createNamedQueryDefinition( queryName, queryString, resultClass,
hints.determineLockOptions( namedQuery ), hints );
hints.determineLockOptions( namedQuery ), hints, annotationTarget );
if ( isDefault ) {
context.getMetadataCollector().addDefaultQuery( queryMapping );
}
@ -105,8 +107,8 @@ public abstract class QueryBinder {
private static <T> NamedHqlQueryDefinitionImpl<T> createNamedQueryDefinition(
String queryName, String queryString, Class<T> resultClass, LockOptions lockOptions,
QueryHintDefinition hints) {
return new NamedHqlQueryDefinitionImpl.Builder<T>(queryName)
QueryHintDefinition hints, AnnotationTarget annotationTarget) {
return new NamedHqlQueryDefinitionImpl.Builder<T>(queryName, annotationTarget)
.setHqlString(queryString)
.setResultClass(resultClass)
.setCacheable(hints.getCacheability())
@ -124,6 +126,7 @@ public abstract class QueryBinder {
public static void bindNativeQuery(
NamedNativeQuery namedNativeQuery,
MetadataBuildingContext context,
AnnotationTarget location,
boolean isDefault) {
if ( namedNativeQuery == null ) {
return;
@ -143,7 +146,7 @@ public abstract class QueryBinder {
final Class<?> resultClass = void.class == resultClassDetails ? null : resultClassDetails;
final NamedNativeQueryDefinition<?> queryDefinition =
createNamedQueryDefinition( registrationName, queryString, resultClass, resultSetMappingName, hints );
createNamedQueryDefinition( registrationName, queryString, resultClass, resultSetMappingName, hints, location );
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Binding named native query: %s => %s",
@ -161,8 +164,9 @@ public abstract class QueryBinder {
private static <T> NamedNativeQueryDefinition<T> createNamedQueryDefinition(
String registrationName, String queryString,
Class<T> resultClass, String resultSetMappingName,
QueryHintDefinition hints) {
return new NamedNativeQueryDefinition.Builder<T>(registrationName)
QueryHintDefinition hints,
AnnotationTarget location) {
return new NamedNativeQueryDefinition.Builder<T>(registrationName, location)
.setSqlString(queryString)
.setResultClass(resultClass)
.setResultSetMappingName(resultSetMappingName)
@ -184,10 +188,11 @@ public abstract class QueryBinder {
SQLSelect sqlSelect,
ClassDetails annotatedClass,
MetadataBuildingContext context) {
final NamedNativeQueryDefinition.Builder<?> builder = new NamedNativeQueryDefinition.Builder<>( name )
.setFlushMode( FlushMode.MANUAL )
.setSqlString( sqlSelect.sql() )
.setQuerySpaces( setOf( sqlSelect.querySpaces() ) );
final NamedNativeQueryDefinition.Builder<?> builder =
new NamedNativeQueryDefinition.Builder<>( name )
.setFlushMode( FlushMode.MANUAL )
.setSqlString( sqlSelect.sql() )
.setQuerySpaces( setOf( sqlSelect.querySpaces() ) );
if ( annotatedClass != null ) {
builder.setResultClass(
@ -210,7 +215,8 @@ public abstract class QueryBinder {
public static void bindNativeQuery(
org.hibernate.annotations.NamedNativeQuery namedNativeQuery,
MetadataBuildingContext context) {
MetadataBuildingContext context,
AnnotationTarget location) {
if ( namedNativeQuery == null ) {
return;
}
@ -231,7 +237,7 @@ public abstract class QueryBinder {
final NamedNativeQueryDefinition.Builder<?> builder =
createQueryDefinition( namedNativeQuery, registrationName, resultSetMappingName, resultClass,
namedNativeQuery.timeout(), namedNativeQuery.fetchSize(), querySpaces );
namedNativeQuery.timeout(), namedNativeQuery.fetchSize(), querySpaces, location );
if ( TRUE == namedNativeQuery.callable() ) {
final NamedProcedureCallDefinition definition =
@ -261,8 +267,9 @@ public abstract class QueryBinder {
String registrationName, String resultSetMappingName,
Class<T> resultClass,
int timeout, int fetchSize,
HashSet<String> querySpaces) {
return new NamedNativeQueryDefinition.Builder<T>(registrationName)
HashSet<String> querySpaces,
AnnotationTarget location) {
return new NamedNativeQueryDefinition.Builder<T>(registrationName, location)
.setSqlString(namedNativeQuery.query())
.setResultSetMappingName(resultSetMappingName)
.setResultClass(resultClass)
@ -365,17 +372,18 @@ public abstract class QueryBinder {
String name,
HQLSelect hqlSelect,
MetadataBuildingContext context) {
final NamedHqlQueryDefinition<?> hqlQueryDefinition = new NamedHqlQueryDefinition.Builder<>( name )
.setFlushMode( FlushMode.MANUAL )
.setHqlString( hqlSelect.query() )
.build();
final NamedHqlQueryDefinition<?> hqlQueryDefinition =
new NamedHqlQueryDefinition.Builder<>( name )
.setFlushMode( FlushMode.MANUAL )
.setHqlString( hqlSelect.query() )
.build();
context.getMetadataCollector().addNamedQuery( hqlQueryDefinition );
}
public static void bindQuery(
org.hibernate.annotations.NamedQuery namedQuery,
MetadataBuildingContext context) {
MetadataBuildingContext context,
AnnotationTarget location) {
if ( namedQuery == null ) {
return;
}
@ -389,7 +397,7 @@ public abstract class QueryBinder {
final NamedHqlQueryDefinition.Builder<?> builder =
createQueryDefinition( namedQuery, registrationName, resultClass,
namedQuery.timeout(), namedQuery.fetchSize() ) ;
namedQuery.timeout(), namedQuery.fetchSize(), location ) ;
final NamedHqlQueryDefinitionImpl<?> hqlQueryDefinition = builder.build();
@ -403,8 +411,9 @@ public abstract class QueryBinder {
private static <T> NamedHqlQueryDefinition.Builder<T> createQueryDefinition(
org.hibernate.annotations.NamedQuery namedQuery,
String registrationName, Class<T> resultClass, int timeout, int fetchSize) {
return new NamedHqlQueryDefinition.Builder<T>(registrationName)
String registrationName, Class<T> resultClass, int timeout, int fetchSize,
AnnotationTarget location) {
return new NamedHqlQueryDefinition.Builder<T>(registrationName, location)
.setHqlString(namedQuery.query())
.setResultClass(resultClass)
.setCacheable(namedQuery.cacheable())

View File

@ -47,15 +47,16 @@ public class NamedQueryBinder {
String prefix) {
final String registrationName = prefix + namedQueryBinding.getName();
final NamedHqlQueryDefinition.Builder<?> queryBuilder = new NamedHqlQueryDefinition.Builder<>( registrationName )
.setComment( namedQueryBinding.getComment() )
.setCacheable( namedQueryBinding.isCacheable() )
.setCacheMode( namedQueryBinding.getCacheMode() )
.setCacheRegion( namedQueryBinding.getCacheRegion() )
.setTimeout( namedQueryBinding.getTimeout() )
.setReadOnly( namedQueryBinding.isReadOnly() )
.setFlushMode( namedQueryBinding.getFlushMode() )
.setFetchSize( namedQueryBinding.getFetchSize() );
final NamedHqlQueryDefinition.Builder<?> queryBuilder =
new NamedHqlQueryDefinition.Builder<>( registrationName )
.setComment( namedQueryBinding.getComment() )
.setCacheable( namedQueryBinding.isCacheable() )
.setCacheMode( namedQueryBinding.getCacheMode() )
.setCacheRegion( namedQueryBinding.getCacheRegion() )
.setTimeout( namedQueryBinding.getTimeout() )
.setReadOnly( namedQueryBinding.isReadOnly() )
.setFlushMode( namedQueryBinding.getFlushMode() )
.setFetchSize( namedQueryBinding.getFetchSize() );
boolean foundQuery = false;
@ -109,16 +110,17 @@ public class NamedQueryBinder {
final String registrationName = prefix + namedQueryBinding.getName();
final NamedNativeQueryDefinition.Builder<?> builder = new NamedNativeQueryDefinition.Builder<>( registrationName )
.setComment( namedQueryBinding.getComment() )
.setCacheable( namedQueryBinding.isCacheable() )
.setCacheMode( namedQueryBinding.getCacheMode() )
.setCacheRegion( namedQueryBinding.getCacheRegion() )
.setTimeout( namedQueryBinding.getTimeout() )
.setReadOnly( namedQueryBinding.isReadOnly() )
.setFlushMode( namedQueryBinding.getFlushMode() )
.setFetchSize( namedQueryBinding.getFetchSize() )
.setResultSetMappingName( namedQueryBinding.getResultsetRef() );
final NamedNativeQueryDefinition.Builder<?> builder =
new NamedNativeQueryDefinition.Builder<>( registrationName )
.setComment( namedQueryBinding.getComment() )
.setCacheable( namedQueryBinding.isCacheable() )
.setCacheMode( namedQueryBinding.getCacheMode() )
.setCacheRegion( namedQueryBinding.getCacheRegion() )
.setTimeout( namedQueryBinding.getTimeout() )
.setReadOnly( namedQueryBinding.isReadOnly() )
.setFlushMode( namedQueryBinding.getFlushMode() )
.setFetchSize( namedQueryBinding.getFetchSize() )
.setResultSetMappingName( namedQueryBinding.getResultsetRef() );
final ImplicitHbmResultSetMappingDescriptorBuilder implicitResultSetMappingBuilder =
new ImplicitHbmResultSetMappingDescriptorBuilder( registrationName, context );
@ -222,12 +224,10 @@ public class NamedQueryBinder {
);
}
if ( content instanceof JaxbHbmQueryParamType ) {
final JaxbHbmQueryParamType paramTypeBinding = (JaxbHbmQueryParamType) content;
if ( content instanceof JaxbHbmQueryParamType paramTypeBinding ) {
queryBuilder.addParameterTypeHint( paramTypeBinding.getName(), paramTypeBinding.getType() );
}
else if ( content instanceof JaxbHbmSynchronizeType ) {
final JaxbHbmSynchronizeType synchronizedSpace = (JaxbHbmSynchronizeType) content;
else if ( content instanceof JaxbHbmSynchronizeType synchronizedSpace ) {
queryBuilder.addSynchronizedQuerySpace( synchronizedSpace.getTable() );
}
else if ( content instanceof JaxbHbmNativeQueryScalarReturnType ) {

View File

@ -12,6 +12,7 @@ import org.hibernate.FlushMode;
import org.hibernate.LockOptions;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.models.spi.AnnotationTarget;
/**
* @author Steve Ebersole
@ -36,14 +37,21 @@ public abstract class AbstractNamedQueryBuilder<R, T extends AbstractNamedQueryB
private Map<String, Object> hints;
public AbstractNamedQueryBuilder(String name) {
private final AnnotationTarget location;
public AbstractNamedQueryBuilder(String name, AnnotationTarget location) {
this.name = name;
this.location = location;
}
public String getName() {
return name;
}
AnnotationTarget getLocation() {
return location;
}
protected abstract T getThis();
public T setResultClass(Class<R> resultClass) {

View File

@ -9,6 +9,7 @@ import java.util.Map;
import org.hibernate.boot.internal.NamedHqlQueryDefinitionImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.query.sqm.spi.NamedSqmQueryMemento;
/**
@ -35,8 +36,12 @@ public interface NamedHqlQueryDefinition<E> extends NamedQueryDefinition<E> {
private Map<String,String> parameterTypes;
public Builder(String name, AnnotationTarget location) {
super( name, location );
}
public Builder(String name) {
super( name );
super( name, null );
}
@Override
@ -80,7 +85,9 @@ public interface NamedHqlQueryDefinition<E> extends NamedQueryDefinition<E> {
getFetchSize(),
getComment(),
parameterTypes,
getHints()
getHints(),
//TODO: should this be location.asClassDetails().getClassName() ?
getLocation() == null ? null : getLocation().getName()
);
}

View File

@ -12,6 +12,7 @@ import java.util.Set;
import org.hibernate.boot.internal.NamedNativeQueryDefinitionImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.query.sql.spi.NamedNativeQueryMemento;
/**
@ -43,8 +44,12 @@ public interface NamedNativeQueryDefinition<E> extends NamedQueryDefinition<E> {
private Integer firstResult;
private Integer maxResults;
public Builder(String name, AnnotationTarget location) {
super( name, location );
}
public Builder(String name) {
super( name );
super( name, null );
}
public Builder<E> setSqlString(String sqlString) {
@ -79,7 +84,9 @@ public interface NamedNativeQueryDefinition<E> extends NamedQueryDefinition<E> {
getComment(),
firstResult,
maxResults,
getHints()
getHints(),
//TODO: should this be location.asClassDetails().getClassName() ?
getLocation() == null ? null : getLocation().getName()
);
}

View File

@ -25,7 +25,7 @@ public interface NamedQueryDefinition<E> extends TypedQueryReference<E> {
}
/**
* The name under which the query is to be registered
* The name under which the query is to be registered.
*/
String getRegistrationName();
@ -36,7 +36,14 @@ public interface NamedQueryDefinition<E> extends TypedQueryReference<E> {
Class<E> getResultType();
/**
* Resolve the mapping definition into its run-time memento form
* Resolve the mapping definition into its run-time memento form.
*/
NamedQueryMemento<E> resolve(SessionFactoryImplementor factory);
/**
* The location at which the defining named query annotation occurs,
* usually a class or package name. Null for named queries declared
* in XML.
*/
@Nullable String getLocation();
}

View File

@ -36,6 +36,7 @@ public abstract class AbstractNamedQueryDefinition<R> implements NamedQueryDefin
private final String comment;
private final Map<String,Object> hints;
private final String location;
public AbstractNamedQueryDefinition(
String name,
@ -49,7 +50,8 @@ public abstract class AbstractNamedQueryDefinition<R> implements NamedQueryDefin
Integer timeout,
Integer fetchSize,
String comment,
Map<String,Object> hints) {
Map<String,Object> hints,
String location) {
this.name = name;
this.resultType = resultType;
this.cacheable = cacheable;
@ -62,6 +64,7 @@ public abstract class AbstractNamedQueryDefinition<R> implements NamedQueryDefin
this.fetchSize = fetchSize;
this.comment = comment;
this.hints = hints == null ? new HashMap<>() : new HashMap<>( hints );
this.location = location;
}
@Override
@ -69,6 +72,11 @@ public abstract class AbstractNamedQueryDefinition<R> implements NamedQueryDefin
return name;
}
@Override
public @Nullable String getLocation() {
return location;
}
@Override
public @Nullable Class<R> getResultType() {
return resultType;

View File

@ -0,0 +1,113 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.metamodel.internal;
import org.hibernate.AssertionFailure;
import org.hibernate.boot.model.NamedEntityGraphDefinition;
import org.hibernate.boot.query.NamedQueryDefinition;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import java.lang.reflect.Field;
import static java.lang.Character.charCount;
public class InjectionHelper {
private static final CoreMessageLogger log = CoreLogging.messageLogger( MetadataContext.class );
public static void injectEntityGraph(
NamedEntityGraphDefinition definition,
Class<?> metamodelClass,
JpaMetamodel jpaMetamodel) {
if ( metamodelClass != null ) {
try {
injectField(
metamodelClass,
'_' + javaIdentifier( definition.getRegisteredName() ),
jpaMetamodel.findEntityGraphByName( definition.getRegisteredName() ),
false
);
}
catch ( NoSuchFieldException e ) {
// ignore
}
}
}
public static void injectTypedQueryReference(NamedQueryDefinition<?> definition, Class<?> metamodelClass) {
if ( metamodelClass != null ) {
try {
injectField(
metamodelClass,
'_' + javaIdentifier( definition.getRegistrationName() ) + '_',
definition,
false
);
}
catch ( NoSuchFieldException e ) {
// ignore
}
}
}
public static String javaIdentifier(String name) {
final StringBuilder result = new StringBuilder();
int position = 0;
while ( position < name.length() ) {
final int codePoint = name.codePointAt( position );
if ( Character.isJavaIdentifierPart(codePoint) ) {
result.appendCodePoint( codePoint );
}
else {
result.append('_');
}
position += charCount( codePoint );
}
return result.toString();
}
public static void injectField(
Class<?> metamodelClass, String name, Object model,
boolean allowNonDeclaredFieldReference)
throws NoSuchFieldException {
final Field field = allowNonDeclaredFieldReference
? metamodelClass.getField(name)
: metamodelClass.getDeclaredField(name);
try {
// should be public anyway, but to be sure...
ReflectHelper.ensureAccessibility( field );
field.set( null, model);
}
catch (IllegalAccessException e) {
// todo : exception type?
throw new AssertionFailure(
"Unable to inject static metamodel attribute : " + metamodelClass.getName() + '#' + name,
e
);
}
catch (IllegalArgumentException e) {
// most likely a mismatch in the type we are injecting and the defined field; this represents a
// mismatch in how the annotation processor interpreted the attribute and how our metamodel
// and/or annotation binder did.
// This is particularly the case as arrays are not handled properly by the StaticMetamodel generator
// throw new AssertionFailure(
// "Illegal argument on static metamodel field injection : " + metamodelClass.getName() + '#' + name
// + "; expected type : " + attribute.getClass().getName()
// + "; encountered type : " + field.getType().getName()
// );
log.illegalArgumentOnStaticMetamodelFieldInjection(
metamodelClass.getName(),
name,
model.getClass().getName(),
field.getType().getName()
);
}
}
}

View File

@ -4,25 +4,18 @@
*/
package org.hibernate.metamodel.internal;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.IdentifiableType;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import org.hibernate.AssertionFailure;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.boot.query.NamedQueryDefinition;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.MappedSuperclass;
@ -53,13 +46,16 @@ import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.IdentifiableType;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import static java.lang.Character.charCount;
import static java.util.Collections.unmodifiableMap;
import static org.hibernate.metamodel.internal.InjectionHelper.injectField;
/**
@ -278,8 +274,9 @@ public class MetadataContext {
attribute = factoryFunction.apply( entityType, genericProperty );
if ( !property.isGeneric() ) {
final PersistentAttribute<X, ?> concreteAttribute = factoryFunction.apply( entityType, property );
//noinspection unchecked
( (AttributeContainer<X>) entityType ).getInFlightAccess().addConcreteGenericAttribute( concreteAttribute );
@SuppressWarnings("unchecked")
final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) entityType;
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteAttribute );
}
}
else {
@ -324,18 +321,7 @@ public class MetadataContext {
// skip the version property, it was already handled previously.
continue;
}
final PersistentAttribute<Object, ?> attribute = buildAttribute(
property,
jpaMapping,
attributeFactory::buildAttribute
);
if ( attribute != null ) {
addAttribute( jpaMapping, attribute );
if ( property.isNaturalIdentifier() ) {
( ( AttributeContainer<Object>) jpaMapping ).getInFlightAccess()
.applyNaturalIdAttribute( attribute );
}
}
buildAttribute( property, jpaMapping );
}
( (AttributeContainer<?>) jpaMapping ).getInFlightAccess().finishUp();
@ -364,21 +350,10 @@ public class MetadataContext {
// applyNaturalIdAttribute( safeMapping, jpaType );
for ( Property property : safeMapping.getDeclaredProperties() ) {
if ( safeMapping.isVersioned() && property == safeMapping.getVersion() ) {
// skip the version property, it was already handled previously.
continue;
}
final PersistentAttribute<Object, ?> attribute = buildAttribute(
property,
jpaType,
attributeFactory::buildAttribute
);
if ( attribute != null ) {
addAttribute( jpaType, attribute );
if ( property.isNaturalIdentifier() ) {
( ( AttributeContainer<Object>) jpaType ).getInFlightAccess()
.applyNaturalIdAttribute( attribute );
}
if ( !safeMapping.isVersioned()
// skip the version property, it was already handled previously.
|| property != safeMapping.getVersion() ) {
buildAttribute( property, jpaType );
}
}
@ -411,22 +386,9 @@ public class MetadataContext {
for ( EmbeddableDomainType<?> embeddable : processingEmbeddables ) {
final Component component = componentByEmbeddable.get( embeddable );
for ( Property property : component.getProperties() ) {
if ( component.isPolymorphic() && !embeddable.getTypeName().equals( component.getPropertyDeclaringClass( property ) ) ) {
continue;
}
final PersistentAttribute<Object, ?> attribute =
attributeFactory.buildAttribute( (ManagedDomainType<Object>) embeddable, property );
if ( attribute != null ) {
final Property superclassProperty = getMappedSuperclassProperty(
property.getName(),
component.getMappedSuperclass()
);
if ( superclassProperty != null && superclassProperty.isGeneric() ) {
( (AttributeContainer<Object>) embeddable ).getInFlightAccess().addConcreteGenericAttribute( attribute );
}
else {
addAttribute( embeddable, attribute );
}
if ( !component.isPolymorphic()
|| embeddable.getTypeName().equals( component.getPropertyDeclaringClass( property ) ) ) {
addAttribute( embeddable, property, component );
}
}
@ -444,20 +406,52 @@ public class MetadataContext {
}
}
private void addAttribute(ManagedDomainType<?> type, PersistentAttribute<Object, ?> attribute) {
//noinspection unchecked
AttributeContainer<Object> container = (AttributeContainer<Object>) type;
final AttributeContainer.InFlightAccess<Object> inFlightAccess = container.getInFlightAccess();
private <T> void addAttribute(EmbeddableDomainType<T> embeddable, Property property, Component component) {
final PersistentAttribute<T, ?> attribute =
attributeFactory.buildAttribute( embeddable, property);
if ( attribute != null ) {
final Property superclassProperty = getMappedSuperclassProperty(
property.getName(),
component.getMappedSuperclass()
);
if ( superclassProperty != null && superclassProperty.isGeneric() ) {
@SuppressWarnings("unchecked")
final AttributeContainer<T> attributeContainer = (AttributeContainer<T>) embeddable;
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( attribute );
}
else {
addAttribute(embeddable, attribute );
}
}
}
private <T> void buildAttribute(Property property, IdentifiableDomainType<T> jpaType) {
final PersistentAttribute<T, ?> attribute =
buildAttribute( property, jpaType, attributeFactory::buildAttribute );
if ( attribute != null ) {
addAttribute(jpaType, attribute );
if ( property.isNaturalIdentifier() ) {
@SuppressWarnings("unchecked")
final AttributeContainer<T> attributeContainer = (AttributeContainer<T>) jpaType;
attributeContainer.getInFlightAccess().applyNaturalIdAttribute( attribute );
}
}
}
private <T> void addAttribute(ManagedDomainType<T> type, PersistentAttribute<T, ?> attribute) {
@SuppressWarnings("unchecked")
final AttributeContainer<T> container = (AttributeContainer<T>) type;
final AttributeContainer.InFlightAccess<T> inFlightAccess = container.getInFlightAccess();
final boolean virtual = attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED
&& attribute.getAttributeJavaType() instanceof EntityJavaType<?>;
if ( virtual ) {
final EmbeddableDomainType<?> embeddableDomainType = (EmbeddableDomainType<?>) attribute.getValueGraphType();
@SuppressWarnings("unchecked")
final EmbeddableDomainType<T> embeddableDomainType =
(EmbeddableDomainType<T>) attribute.getValueGraphType();
final Component component = componentByEmbeddable.get( embeddableDomainType );
for ( Property property : component.getProperties() ) {
//noinspection unchecked
ManagedDomainType<Object> managedDomainType = (ManagedDomainType<Object>) embeddableDomainType;
final PersistentAttribute<Object, ?> subAttribute =
attributeFactory.buildAttribute( managedDomainType, property );
final PersistentAttribute<T, ?> subAttribute =
attributeFactory.buildAttribute( embeddableDomainType, property );
if ( subAttribute != null ) {
inFlightAccess.addAttribute( subAttribute );
}
@ -473,43 +467,41 @@ public class MetadataContext {
// 2) register the part (mapping role)
// 3) somehow get the mapping role "into" the part (setter, ?)
private void applyIdMetadata(PersistentClass persistentClass, IdentifiableDomainType<?> identifiableType) {
private <T> void applyIdMetadata(PersistentClass persistentClass, IdentifiableDomainType<T> identifiableType) {
if ( persistentClass.hasIdentifierProperty() ) {
final Property declaredIdentifierProperty = persistentClass.getDeclaredIdentifierProperty();
//noinspection rawtypes
final AttributeContainer attributeContainer = (AttributeContainer) identifiableType;
@SuppressWarnings("unchecked")
final AttributeContainer<T> attributeContainer = (AttributeContainer<T>) identifiableType;
if ( declaredIdentifierProperty != null ) {
//noinspection unchecked
final SingularPersistentAttribute<?, Object> idAttribute = (SingularPersistentAttribute<?, Object>) buildAttribute(
declaredIdentifierProperty,
identifiableType,
attributeFactory::buildIdAttribute
);
//noinspection unchecked
final SingularPersistentAttribute<T, ?> idAttribute =
(SingularPersistentAttribute<T, ?>)
buildAttribute(
declaredIdentifierProperty,
identifiableType,
attributeFactory::buildIdAttribute
);
attributeContainer.getInFlightAccess().applyIdAttribute( idAttribute );
}
else {
final Property superclassIdentifier = getMappedSuperclassIdentifier( persistentClass );
if ( superclassIdentifier != null && superclassIdentifier.isGeneric() ) {
// If the superclass identifier is generic we have to build the attribute to register the concrete type
final SingularPersistentAttribute<?, Object> concreteIdentifier = attributeFactory.buildIdAttribute(
identifiableType,
persistentClass.getIdentifierProperty()
);
//noinspection unchecked
final SingularPersistentAttribute<T, ?> concreteIdentifier =
attributeFactory.buildIdAttribute(
identifiableType,
persistentClass.getIdentifierProperty()
);
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteIdentifier );
}
}
}
else {
// we have a non-aggregated composite-id
if ( !(persistentClass.getIdentifier() instanceof Component) ) {
if ( !( persistentClass.getIdentifier() instanceof Component compositeId ) ) {
throw new MappingException( "Expecting Component for id mapping with no id-attribute" );
}
// Handle the actual id-attributes
final Component cidValue = (Component) persistentClass.getIdentifier();
final List<Property> cidProperties;
final int propertySpan;
final EmbeddableTypeImpl<?> idClassType;
@ -520,34 +512,40 @@ public class MetadataContext {
idClassType = applyIdClassMetadata( (Component) persistentClass.getIdentifier() );
}
else {
cidProperties = cidValue.getProperties();
propertySpan = cidValue.getPropertySpan();
cidProperties = compositeId.getProperties();
propertySpan = compositeId.getPropertySpan();
idClassType = null;
}
assert cidValue.isEmbedded();
assert compositeId.isEmbedded();
AbstractIdentifiableType<?> idType = (AbstractIdentifiableType<?>)
identifiableTypesByName.get( cidValue.getOwner().getEntityName() );
//noinspection rawtypes
Set idAttributes = idType.getIdClassAttributesSafely();
if ( idAttributes == null ) {
idAttributes = new HashSet<>( propertySpan );
for ( Property cidSubproperty : cidProperties ) {
final SingularPersistentAttribute<?, Object> cidSubAttr =
attributeFactory.buildIdAttribute( idType, cidSubproperty );
//noinspection unchecked
idAttributes.add( cidSubAttr );
}
}
AttributeContainer<?> container = (AttributeContainer<?>) identifiableType;
//noinspection unchecked
container.getInFlightAccess().applyNonAggregatedIdAttributes( idAttributes, idClassType );
final IdentifiableDomainType<?> idDomainType =
identifiableTypesByName.get( compositeId.getOwner().getEntityName() );
@SuppressWarnings("unchecked")
final AbstractIdentifiableType<T> idType = (AbstractIdentifiableType<T>) idDomainType;
applyIdAttributes( identifiableType, idType, propertySpan, cidProperties, idClassType );
}
}
private <T> void applyIdAttributes(
IdentifiableDomainType<T> identifiableType,
AbstractIdentifiableType<T> idType,
int propertySpan,
List<Property> cidProperties,
EmbeddableTypeImpl<?> idClassType) {
Set<SingularPersistentAttribute<? super T, ?>> idAttributes = idType.getIdClassAttributesSafely();
if ( idAttributes == null ) {
idAttributes = new HashSet<>( propertySpan );
for ( Property cidSubproperty : cidProperties ) {
idAttributes.add( attributeFactory.buildIdAttribute( idType, cidSubproperty ) );
}
}
@SuppressWarnings("unchecked")
final AttributeContainer<T> container = (AttributeContainer<T>) identifiableType;
container.getInFlightAccess().applyNonAggregatedIdAttributes( idAttributes, idClassType);
}
private Property getMappedSuperclassIdentifier(PersistentClass persistentClass) {
MappedSuperclass mappedSuperclass = getMappedSuperclass( persistentClass );
while ( mappedSuperclass != null ) {
@ -561,52 +559,48 @@ public class MetadataContext {
}
private EmbeddableTypeImpl<?> applyIdClassMetadata(Component idClassComponent) {
final JavaTypeRegistry registry = getTypeConfiguration()
.getJavaTypeRegistry();
final Class<?> componentClass = idClassComponent.getComponentClass();
final JavaType<?> javaType = registry.resolveManagedTypeDescriptor( componentClass );
final EmbeddableTypeImpl<?> embeddableType = new EmbeddableTypeImpl<>(
javaType,
null,
null,
false,
getJpaMetamodel()
);
final JavaType<?> javaType =
getTypeConfiguration().getJavaTypeRegistry()
.resolveManagedTypeDescriptor( idClassComponent.getComponentClass() );
final EmbeddableTypeImpl<?> embeddableType =
new EmbeddableTypeImpl<>( javaType, null, null, false, getJpaMetamodel() );
registerEmbeddableType( embeddableType, idClassComponent );
return embeddableType;
}
private <X> void applyIdMetadata(MappedSuperclass mappingType, MappedSuperclassDomainType<X> jpaMappingType) {
@SuppressWarnings("unchecked")
final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) jpaMappingType;
if ( mappingType.hasIdentifierProperty() ) {
final Property declaredIdentifierProperty = mappingType.getDeclaredIdentifierProperty();
if ( declaredIdentifierProperty != null ) {
//noinspection unchecked
final SingularPersistentAttribute<X, Object> attribute = (SingularPersistentAttribute<X, Object>) buildAttribute(
declaredIdentifierProperty,
jpaMappingType,
attributeFactory::buildIdAttribute
);
//noinspection unchecked
( (AttributeContainer<X>) jpaMappingType ).getInFlightAccess().applyIdAttribute( attribute );
final SingularPersistentAttribute<X, ?> attribute =
(SingularPersistentAttribute<X, ?>)
buildAttribute(
declaredIdentifierProperty,
jpaMappingType,
attributeFactory::buildIdAttribute
);
attributeContainer.getInFlightAccess().applyIdAttribute( attribute );
}
}
//a MappedSuperclass can have no identifier if the id is set below in the hierarchy
else if ( mappingType.getIdentifierMapper() != null ) {
Set<SingularPersistentAttribute<? super X, ?>> attributes = buildIdClassAttributes(
jpaMappingType,
mappingType.getIdentifierMapper().getProperties()
);
//noinspection unchecked
( ( AttributeContainer<X>) jpaMappingType ).getInFlightAccess().applyIdClassAttributes( attributes );
final Set<SingularPersistentAttribute<? super X, ?>> attributes =
buildIdClassAttributes(
jpaMappingType,
mappingType.getIdentifierMapper().getProperties()
);
attributeContainer.getInFlightAccess().applyIdClassAttributes( attributes );
}
}
private <X> void applyVersionAttribute(PersistentClass persistentClass, EntityDomainType<X> jpaEntityType) {
final Property declaredVersion = persistentClass.getDeclaredVersion();
if ( declaredVersion != null ) {
//noinspection unchecked
( ( AttributeContainer<X>) jpaEntityType ).getInFlightAccess().applyVersionAttribute(
@SuppressWarnings("unchecked")
final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) jpaEntityType;
attributeContainer.getInFlightAccess().applyVersionAttribute(
attributeFactory.buildVersionAttribute( jpaEntityType, declaredVersion )
);
}
@ -615,8 +609,9 @@ public class MetadataContext {
private <X> void applyVersionAttribute(MappedSuperclass mappingType, MappedSuperclassDomainType<X> jpaMappingType) {
final Property declaredVersion = mappingType.getDeclaredVersion();
if ( declaredVersion != null ) {
//noinspection unchecked
( ( AttributeContainer<X>) jpaMappingType ).getInFlightAccess().applyVersionAttribute(
@SuppressWarnings("unchecked")
final AttributeContainer<X> xAttributeContainer = (AttributeContainer<X>) jpaMappingType;
xAttributeContainer.getInFlightAccess().applyVersionAttribute(
attributeFactory.buildVersionAttribute( jpaMappingType, declaredVersion )
);
}
@ -629,8 +624,9 @@ public class MetadataContext {
if ( superclassProperty.isGeneric() ) {
final Property property = persistentClass.getProperty( superclassProperty.getName() );
final PersistentAttribute<X, ?> attribute = attributeFactory.buildAttribute( entityType, property );
//noinspection unchecked
( (AttributeContainer<X>) entityType ).getInFlightAccess().addConcreteGenericAttribute( attribute );
@SuppressWarnings("unchecked")
final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) entityType;
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( attribute );
}
}
mappedSuperclass = getMappedSuperclass( mappedSuperclass );
@ -695,13 +691,9 @@ public class MetadataContext {
private <X> void populateStaticMetamodel(ManagedDomainType<X> managedType, Set<String> processedMetamodelClassName) {
final Class<X> managedTypeClass = managedType.getJavaType();
if ( managedTypeClass == null ) {
// should indicate MAP entity mode, skip...
return;
}
final String metamodelClassName = managedTypeClass.getName() + '_';
if ( processedMetamodelClassName.add( metamodelClassName ) ) {
final Class<?> metamodelClass = metamodelClass( metamodelClassName );
if ( managedTypeClass != null // can be null for MAP entity mode, so skip...
&& processedMetamodelClassName.add( metamodelClassName( managedType ) ) ) {
final Class<?> metamodelClass = metamodelClass( managedType );
if ( metamodelClass != null ) {
populateMetamodelClass( managedType, metamodelClass );
}
@ -718,61 +710,34 @@ public class MetadataContext {
private <X> void populateMetamodelClass(ManagedDomainType<X> managedType, Class<?> metamodelClass) {
registerAttributes( metamodelClass, managedType );
injectManagedType( managedType, metamodelClass );
runtimeModelCreationContext.getBootModel()
.visitNamedHqlQueryDefinitions( definition
-> injectTypedQueryReference( definition, metamodelClass) );
runtimeModelCreationContext.getBootModel()
.visitNamedNativeQueryDefinitions( definition
-> injectTypedQueryReference( definition, metamodelClass) );
//TODO: named entity graphs
}
private static <X> void injectManagedType(ManagedDomainType<X> managedType, Class<?> metamodelClass) {
try {
injectField(metamodelClass, "class_", managedType, false );
injectField( metamodelClass, "class_", managedType, false );
}
catch ( NoSuchFieldException e ) {
// ignore
}
}
private Class<?> metamodelClass(String metamodelClassName) {
try {
return classLoaderService.classForName( metamodelClassName );
}
catch ( ClassLoadingException ignore ) {
private static String metamodelClassName(ManagedDomainType<?> managedTypeClass) {
return managedTypeClass.getJavaType().getName() + '_';
}
public Class<?> metamodelClass(ManagedDomainType<?> managedDomainType) {
if ( managedDomainType == null ) {
return null;
}
}
private static void injectTypedQueryReference(NamedQueryDefinition<?> definition, Class<?> metamodelClass) {
try {
injectField(
metamodelClass,
'_' + javaIdentifier( definition.getRegistrationName() ) + '_',
definition,
false
);
}
catch ( NoSuchFieldException e ) {
// ignore
}
}
public static String javaIdentifier(String name) {
final StringBuilder result = new StringBuilder();
int position = 0;
while ( position < name.length() ) {
final int codePoint = name.codePointAt( position );
if ( Character.isJavaIdentifierPart(codePoint) ) {
result.appendCodePoint( codePoint );
else {
final String metamodelClassName = metamodelClassName( managedDomainType );
try {
return classLoaderService.classForName( metamodelClassName );
}
else {
result.append('_');
catch ( ClassLoadingException ignore ) {
return null;
}
position += charCount( codePoint );
}
return result.toString();
}
private <X> void registerAttributes(Class<?> metamodelClass, ManagedDomainType<X> managedType) {
@ -826,45 +791,6 @@ public class MetadataContext {
}
}
private static void injectField(
Class<?> metamodelClass, String name, Object model,
boolean allowNonDeclaredFieldReference)
throws NoSuchFieldException {
final Field field = allowNonDeclaredFieldReference
? metamodelClass.getField(name)
: metamodelClass.getDeclaredField(name);
try {
// should be public anyway, but to be sure...
ReflectHelper.ensureAccessibility( field );
field.set( null, model);
}
catch (IllegalAccessException e) {
// todo : exception type?
throw new AssertionFailure(
"Unable to inject static metamodel attribute : " + metamodelClass.getName() + '#' + name,
e
);
}
catch (IllegalArgumentException e) {
// most likely a mismatch in the type we are injecting and the defined field; this represents a
// mismatch in how the annotation processor interpreted the attribute and how our metamodel
// and/or annotation binder did.
// This is particularly the case as arrays are not handled properly by the StaticMetamodel generator
// throw new AssertionFailure(
// "Illegal argument on static metamodel field injection : " + metamodelClass.getName() + '#' + name
// + "; expected type : " + attribute.getClass().getName()
// + "; encountered type : " + field.getType().getName()
// );
log.illegalArgumentOnStaticMetamodelFieldInjection(
metamodelClass.getName(),
name,
model.getClass().getName(),
field.getType().getName()
);
}
}
public MappedSuperclassDomainType<?> locateMappedSuperclassType(MappedSuperclass mappedSuperclass) {
return mappedSuperclassByMappedSuperclassMapping.get( mappedSuperclass );
@ -879,10 +805,8 @@ public class MetadataContext {
stackOfPersistentClassesBeingProcessed.size() - 1
);
if ( stackTop != persistentClass ) {
throw new AssertionFailure(
"Inconsistent popping: "
+ persistentClass.getEntityName() + " instead of " + stackTop.getEntityName()
);
throw new AssertionFailure( "Inconsistent popping: "
+ persistentClass.getEntityName() + " instead of " + stackTop.getEntityName() );
}
}
@ -903,20 +827,25 @@ public class MetadataContext {
private final Map<Class<?>,BasicDomainType<?>> basicDomainTypeMap = new HashMap<>();
public <J> BasicDomainType<J> resolveBasicType(Class<J> javaType) {
//noinspection unchecked
return (BasicDomainType<J>) basicDomainTypeMap.computeIfAbsent(
javaType,
jt -> {
// we cannot use getTypeConfiguration().standardBasicTypeForJavaType(javaType)
// because that doesn't return the right thing for primitive types
final JavaTypeRegistry registry = getTypeConfiguration().getJavaTypeRegistry();
JavaType<J> javaTypeDescriptor = registry.resolveDescriptor( javaType );
JdbcType jdbcType = javaTypeDescriptor.getRecommendedJdbcType( typeConfiguration.getCurrentBaseSqlTypeIndicators() );
return javaType.isPrimitive()
? new PrimitiveBasicTypeImpl<>( javaTypeDescriptor, jdbcType , javaType )
@SuppressWarnings("unchecked")
final BasicDomainType<J> domainType = (BasicDomainType<J>) basicDomainTypeMap.get( javaType );
if ( domainType == null ) {
// we cannot use getTypeConfiguration().standardBasicTypeForJavaType(javaType)
// because that doesn't return the right thing for primitive types
final JavaType<J> javaTypeDescriptor =
getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor( javaType );
final JdbcType jdbcType =
javaTypeDescriptor.getRecommendedJdbcType( typeConfiguration.getCurrentBaseSqlTypeIndicators() );
final BasicDomainType<J> type =
javaType.isPrimitive()
? new PrimitiveBasicTypeImpl<>( javaTypeDescriptor, jdbcType, javaType )
: new BasicTypeImpl<>( javaTypeDescriptor, jdbcType );
}
);
basicDomainTypeMap.put( javaType, type );
return type;
}
else {
return domainType;
}
}
public <J> EmbeddableDomainType<J> locateEmbeddable(Class<J> embeddableClass, Component component) {

View File

@ -21,6 +21,7 @@ import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.boot.model.NamedEntityGraphDefinition;
import org.hibernate.boot.query.NamedQueryDefinition;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.MetadataImplementor;
@ -32,7 +33,6 @@ import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.graph.spi.SubGraphImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.jpa.spi.JpaCompliance;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
@ -68,6 +68,9 @@ import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.metamodel.Type;
import static java.util.Collections.emptySet;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.metamodel.internal.InjectionHelper.injectEntityGraph;
import static org.hibernate.metamodel.internal.InjectionHelper.injectTypedQueryReference;
/**
*
@ -77,10 +80,10 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
private static final CoreMessageLogger log = CoreLogging.messageLogger( JpaMetamodel.class );
private static class ImportInfo<T> {
final String importedName;
Class<T> loadedClass; // could be null for boot metamodel import; not final to allow for populating later
private final String importedName;
private Class<T> loadedClass; // could be null for boot metamodel import; not final to allow for populating later
ImportInfo(String importedName, Class<T> loadedClass) {
private ImportInfo(String importedName, Class<T> loadedClass) {
this.importedName = importedName;
this.loadedClass = loadedClass;
}
@ -301,6 +304,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
}
}
catch (NoSuchFieldException e) {
// ignore
}
return null;
}
@ -429,7 +433,6 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
}
}
@SuppressWarnings("unchecked")
private void applyNamedEntityGraphs(Collection<NamedEntityGraphDefinition> namedEntityGraphs) {
for ( NamedEntityGraphDefinition definition : namedEntityGraphs ) {
log.debugf(
@ -438,7 +441,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
definition.getEntityName(),
definition.getJpaEntityName()
);
final EntityDomainType<Object> entityType = (EntityDomainType<Object>) entity( definition.getEntityName() );
final EntityDomainType<?> entityType = entity( definition.getEntityName() );
if ( entityType == null ) {
throw new IllegalArgumentException(
"Attempted to register named entity graph [" + definition.getRegisteredName()
@ -446,47 +449,47 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
);
}
final RootGraphImpl<Object> entityGraph = new RootGraphImpl<>( definition.getRegisteredName(), entityType );
final NamedEntityGraph namedEntityGraph = definition.getAnnotation();
if ( namedEntityGraph.includeAllAttributes() ) {
for ( Attribute<? super Object, ?> attribute : entityType.getAttributes() ) {
entityGraph.addAttributeNodes( attribute );
}
}
final RootGraphImpl<?> entityGraph =
createRootGraph( definition.getRegisteredName(), entityType,
namedEntityGraph.includeAllAttributes() );
if ( namedEntityGraph.attributeNodes() != null ) {
applyNamedAttributeNodes( namedEntityGraph.attributeNodes(), namedEntityGraph, entityGraph );
}
entityGraphMap.put( definition.getRegisteredName(), entityGraph );
}
}
private static <T> RootGraphImpl<T> createRootGraph(
String name, EntityDomainType<T> entityType, boolean includeAllAttributes) {
final RootGraphImpl<T> entityGraph = new RootGraphImpl<>( name, entityType );
if ( includeAllAttributes ) {
for ( Attribute<? super T, ?> attribute : entityType.getAttributes() ) {
entityGraph.addAttributeNodes( attribute );
}
}
return entityGraph;
}
private void applyNamedAttributeNodes(
NamedAttributeNode[] namedAttributeNodes,
NamedEntityGraph namedEntityGraph,
GraphImplementor<?> graphNode) {
for ( NamedAttributeNode namedAttributeNode : namedAttributeNodes ) {
final String value = namedAttributeNode.value();
final AttributeNodeImplementor<?> attributeNode = graphNode.findOrCreateAttributeNode( value );
if ( StringHelper.isNotEmpty( namedAttributeNode.subgraph() ) ) {
final SubGraphImplementor<?> subgraph = attributeNode.makeSubGraph();
final AttributeNodeImplementor<?> attributeNode =
graphNode.findOrCreateAttributeNode( namedAttributeNode.value() );
if ( isNotEmpty( namedAttributeNode.subgraph() ) ) {
applyNamedSubgraphs(
namedEntityGraph,
namedAttributeNode.subgraph(),
subgraph
attributeNode.makeSubGraph()
);
}
if ( StringHelper.isNotEmpty( namedAttributeNode.keySubgraph() ) ) {
final SubGraphImplementor<?> subgraph = attributeNode.makeKeySubGraph();
if ( isNotEmpty( namedAttributeNode.keySubgraph() ) ) {
applyNamedSubgraphs(
namedEntityGraph,
namedAttributeNode.keySubgraph(),
subgraph
attributeNode.makeKeySubGraph()
);
}
}
@ -646,8 +649,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
typeConfiguration.getJavaTypeRegistry().forEachDescriptor( descriptor -> {
if ( descriptor instanceof EnumJavaType<? extends Enum<?>> enumJavaType ) {
final Class<? extends Enum<?>> enumJavaClass = enumJavaType.getJavaTypeClass();
final Enum<?>[] enumConstants = enumJavaClass.getEnumConstants();
for ( Enum<?> enumConstant : enumConstants ) {
for ( Enum<?> enumConstant : enumJavaClass.getEnumConstants() ) {
addAllowedEnumLiteralsToEnumTypesMap(
allowedEnumLiteralsToEnumTypeNames,
enumConstant.name(),
@ -662,6 +664,26 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
} );
applyNamedEntityGraphs( namedEntityGraphDefinitions );
populateStaticMetamodel( bootMetamodel, context );
}
private void populateStaticMetamodel(MetadataImplementor bootMetamodel, MetadataContext context) {
bootMetamodel.visitNamedHqlQueryDefinitions( definition
-> injectTypedQueryReference( definition, namedQueryMetamodelClass( definition, context ) ) );
bootMetamodel.visitNamedNativeQueryDefinitions( definition
-> injectTypedQueryReference( definition, namedQueryMetamodelClass( definition, context ) ) );
bootMetamodel.getNamedEntityGraphs().values().forEach(definition
-> injectEntityGraph( definition, graphMetamodelClass( definition, context ), this ) );
}
private Class<?> namedQueryMetamodelClass(NamedQueryDefinition<?> definition, MetadataContext context) {
final String location = definition.getLocation();
return location == null ? null : context.metamodelClass( managedTypeByName.get( location ) );
}
private Class<?> graphMetamodelClass(NamedEntityGraphDefinition definition, MetadataContext context) {
return context.metamodelClass( managedTypeByName.get( definition.getEntityName() ) );
}
public static void addAllowedEnumLiteralsToEnumTypesMap(

View File

@ -5,5 +5,6 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
@NamedQuery(name = "allMouse",
query = "select m from ApplicationServer m") package org.hibernate.orm.test.jpa.pack.defaultpar;
query = "select m from ApplicationServer m")
package org.hibernate.orm.test.jpa.pack.defaultpar;
import org.hibernate.annotations.NamedQuery;