big refactoring of Binders
This commit is contained in:
parent
dda88668e8
commit
c9cd12c625
|
@ -45,7 +45,7 @@ import jakarta.persistence.JoinColumn;
|
|||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
||||
import static org.hibernate.boot.model.internal.AnnotationBinder.useColumnForTimeZoneStorage;
|
||||
import static org.hibernate.boot.model.internal.TimeZoneStorageHelper.useColumnForTimeZoneStorage;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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 jakarta.persistence.Column;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.annotations.Cascade;
|
||||
import org.hibernate.annotations.Columns;
|
||||
import org.hibernate.annotations.Formula;
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.Property;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
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.BinderHelper.getPath;
|
||||
|
||||
public class AnyBinder {
|
||||
|
||||
static void bindAny(
|
||||
PropertyHolder propertyHolder,
|
||||
Nullability nullability,
|
||||
PropertyData inferredData,
|
||||
EntityBinder entityBinder,
|
||||
boolean isIdentifierMapper,
|
||||
MetadataBuildingContext context,
|
||||
XProperty property,
|
||||
AnnotatedJoinColumns joinColumns,
|
||||
boolean forcePersist) {
|
||||
|
||||
//check validity
|
||||
if ( property.isAnnotationPresent( Columns.class ) ) {
|
||||
throw new AnnotationException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Property '%s' is annotated '@Any' and may not have a '@Columns' annotation "
|
||||
+ "(a single '@Column' or '@Formula' must be used to map the discriminator, and '@JoinColumn's must be used to map the foreign key) ",
|
||||
getPath( propertyHolder, inferredData )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final Cascade hibernateCascade = property.getAnnotation( Cascade.class );
|
||||
final OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
|
||||
final JoinTable assocTable = propertyHolder.getJoinTable(property);
|
||||
if ( assocTable != null ) {
|
||||
final Join join = propertyHolder.addJoin( assocTable, false );
|
||||
for ( AnnotatedJoinColumn joinColumn : joinColumns.getJoinColumns() ) {
|
||||
joinColumn.setExplicitTableName( join.getTable().getName() );
|
||||
}
|
||||
}
|
||||
bindAny(
|
||||
getCascadeStrategy( null, hibernateCascade, false, forcePersist ),
|
||||
//@Any has no cascade attribute
|
||||
joinColumns,
|
||||
onDeleteAnn == null ? null : onDeleteAnn.action(),
|
||||
nullability,
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
entityBinder,
|
||||
isIdentifierMapper,
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
private static void bindAny(
|
||||
String cascadeStrategy,
|
||||
AnnotatedJoinColumns columns,
|
||||
OnDeleteAction onDeleteAction,
|
||||
Nullability nullability,
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData inferredData,
|
||||
EntityBinder entityBinder,
|
||||
boolean isIdentifierMapper,
|
||||
MetadataBuildingContext context) {
|
||||
final XProperty property = inferredData.getProperty();
|
||||
final org.hibernate.annotations.Any any = property.getAnnotation( org.hibernate.annotations.Any.class );
|
||||
if ( any == null ) {
|
||||
throw new AssertionFailure( "Missing @Any annotation: " + getPath( propertyHolder, inferredData ) );
|
||||
}
|
||||
|
||||
final boolean lazy = any.fetch() == FetchType.LAZY;
|
||||
final Any value = BinderHelper.buildAnyValue(
|
||||
property.getAnnotation( Column.class ),
|
||||
getOverridableAnnotation( property, Formula.class, context ),
|
||||
columns,
|
||||
inferredData,
|
||||
onDeleteAction,
|
||||
lazy,
|
||||
nullability,
|
||||
propertyHolder,
|
||||
entityBinder,
|
||||
any.optional(),
|
||||
context
|
||||
);
|
||||
|
||||
final PropertyBinder binder = new PropertyBinder();
|
||||
binder.setName( inferredData.getPropertyName() );
|
||||
binder.setValue( value );
|
||||
binder.setLazy( lazy );
|
||||
//binder.setCascade(cascadeStrategy);
|
||||
if ( isIdentifierMapper ) {
|
||||
binder.setInsertable( false );
|
||||
binder.setUpdatable( false );
|
||||
}
|
||||
binder.setAccessType( inferredData.getDefaultAccess() );
|
||||
binder.setCascade( cascadeStrategy );
|
||||
Property prop = binder.makeProperty();
|
||||
//composite FK columns are in the same table, so it's OK
|
||||
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
|
||||
}
|
||||
}
|
|
@ -14,8 +14,6 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.FetchType;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -96,6 +94,8 @@ import jakarta.persistence.Version;
|
|||
import static org.hibernate.boot.model.internal.HCANNHelper.findAnnotation;
|
||||
|
||||
/**
|
||||
* A stateful binder responsible for creating instances of {@link BasicValue}.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
|
@ -107,26 +107,6 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, BasicValueBinder.class.getName() );
|
||||
|
||||
static boolean isOptional(XProperty property) {
|
||||
if ( property.isAnnotationPresent( Basic.class ) ) {
|
||||
final Basic basic = property.getAnnotation( Basic.class );
|
||||
return basic.optional();
|
||||
}
|
||||
else {
|
||||
return property.isArray() || !property.getClassOrElementClass().isPrimitive();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isLazy(XProperty property) {
|
||||
if ( property.isAnnotationPresent( Basic.class ) ) {
|
||||
final Basic basic = property.getAnnotation( Basic.class );
|
||||
return basic.fetch() == FetchType.LAZY;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Kind {
|
||||
ATTRIBUTE( ValueMappingAccess.INSTANCE ),
|
||||
ANY_DISCRIMINATOR( AnyDiscriminatorMappingAccess.INSTANCE ),
|
||||
|
|
|
@ -22,6 +22,8 @@ import java.util.StringTokenizer;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.FetchMode;
|
||||
|
@ -37,14 +39,11 @@ import org.hibernate.annotations.SqlFragmentAlias;
|
|||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.id.PersistentIdentifierGenerator;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.BasicValue;
|
||||
import org.hibernate.mapping.Collection;
|
||||
|
@ -55,18 +54,13 @@ import org.hibernate.mapping.MappedSuperclass;
|
|||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.SyntheticProperty;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToOne;
|
||||
|
||||
|
@ -74,7 +68,6 @@ import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnOrFor
|
|||
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
|
||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
|
||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.NOOP;
|
||||
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.interpret;
|
||||
|
@ -84,8 +77,6 @@ import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.
|
|||
*/
|
||||
public class BinderHelper {
|
||||
|
||||
private static final Logger log = CoreLogging.logger( BinderHelper.class );
|
||||
|
||||
private BinderHelper() {
|
||||
}
|
||||
|
||||
|
@ -731,129 +722,6 @@ public class BinderHelper {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an id generation strategy and parameters to the
|
||||
* given {@link SimpleValue} which represents an identifier.
|
||||
*/
|
||||
public static void makeIdGenerator(
|
||||
SimpleValue id,
|
||||
XProperty property,
|
||||
String generatorType,
|
||||
String generatorName,
|
||||
MetadataBuildingContext buildingContext,
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators) {
|
||||
log.debugf( "#makeIdGenerator(%s, %s, %s, %s, ...)", id, property, generatorType, generatorName );
|
||||
|
||||
final Table table = id.getTable();
|
||||
table.setIdentifierValue( id );
|
||||
//generator settings
|
||||
id.setIdentifierGeneratorStrategy( generatorType );
|
||||
|
||||
final Map<String,Object> parameters = new HashMap<>();
|
||||
|
||||
//always settable
|
||||
parameters.put( PersistentIdentifierGenerator.TABLE, table.getName() );
|
||||
|
||||
if ( id.getColumnSpan() == 1 ) {
|
||||
parameters.put( PersistentIdentifierGenerator.PK, id.getColumns().get(0).getName() );
|
||||
}
|
||||
// YUCK! but cannot think of a clean way to do this given the string-config based scheme
|
||||
parameters.put( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, buildingContext.getObjectNameNormalizer() );
|
||||
parameters.put( IdentifierGenerator.GENERATOR_NAME, generatorName );
|
||||
|
||||
if ( !generatorName.isEmpty() ) {
|
||||
//we have a named generator
|
||||
final IdentifierGeneratorDefinition definition = makeIdentifierGeneratorDefinition(
|
||||
generatorName,
|
||||
property,
|
||||
localGenerators,
|
||||
buildingContext
|
||||
);
|
||||
if ( definition == null ) {
|
||||
throw new AnnotationException( "No id generator was declared with the name '" + generatorName
|
||||
+ "' specified by '@GeneratedValue'"
|
||||
+ " (define a named generator using '@SequenceGenerator', '@TableGenerator', or '@GenericGenerator')" );
|
||||
}
|
||||
//This is quite vague in the spec but a generator could override the generator choice
|
||||
final String identifierGeneratorStrategy = definition.getStrategy();
|
||||
//yuk! this is a hack not to override 'AUTO' even if generator is set
|
||||
final boolean avoidOverriding = identifierGeneratorStrategy.equals( "identity" )
|
||||
|| identifierGeneratorStrategy.equals( "seqhilo" );
|
||||
if ( generatorType == null || !avoidOverriding ) {
|
||||
id.setIdentifierGeneratorStrategy( identifierGeneratorStrategy );
|
||||
if ( identifierGeneratorStrategy.equals( "assigned" ) ) {
|
||||
id.setNullValue( "undefined" );
|
||||
}
|
||||
}
|
||||
//checkIfMatchingGenerator(definition, generatorType, generatorName);
|
||||
parameters.putAll( definition.getParameters() );
|
||||
}
|
||||
if ( DEFAULT_ID_GEN_STRATEGY.equals( generatorType ) ) {
|
||||
id.setNullValue( "undefined" );
|
||||
}
|
||||
id.setIdentifierGeneratorParameters( parameters );
|
||||
}
|
||||
|
||||
/**
|
||||
* apply an id generator to a SimpleValue
|
||||
*/
|
||||
public static void makeIdGenerator(
|
||||
SimpleValue id,
|
||||
XProperty idXProperty,
|
||||
String generatorType,
|
||||
String generatorName,
|
||||
MetadataBuildingContext buildingContext,
|
||||
IdentifierGeneratorDefinition foreignKGeneratorDefinition) {
|
||||
Map<String, IdentifierGeneratorDefinition> localIdentifiers = null;
|
||||
if ( foreignKGeneratorDefinition != null ) {
|
||||
localIdentifiers = new HashMap<>();
|
||||
localIdentifiers.put( foreignKGeneratorDefinition.getName(), foreignKGeneratorDefinition );
|
||||
}
|
||||
makeIdGenerator( id, idXProperty, generatorType, generatorName, buildingContext, localIdentifiers );
|
||||
}
|
||||
|
||||
private static IdentifierGeneratorDefinition makeIdentifierGeneratorDefinition(
|
||||
String name,
|
||||
XProperty idXProperty,
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators,
|
||||
MetadataBuildingContext buildingContext) {
|
||||
if ( localGenerators != null ) {
|
||||
final IdentifierGeneratorDefinition result = localGenerators.get( name );
|
||||
if ( result != null ) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
final IdentifierGeneratorDefinition globalDefinition =
|
||||
buildingContext.getMetadataCollector().getIdentifierGenerator( name );
|
||||
if ( globalDefinition != null ) {
|
||||
return globalDefinition;
|
||||
}
|
||||
|
||||
log.debugf( "Could not resolve explicit IdentifierGeneratorDefinition - using implicit interpretation (%s)", name );
|
||||
|
||||
final GeneratedValue generatedValue = idXProperty.getAnnotation( GeneratedValue.class );
|
||||
if ( generatedValue == null ) {
|
||||
// this should really never happen, but it's easy to protect against it...
|
||||
return new IdentifierGeneratorDefinition( DEFAULT_ID_GEN_STRATEGY, DEFAULT_ID_GEN_STRATEGY );
|
||||
}
|
||||
|
||||
return IdentifierGeneratorDefinition.createImplicit(
|
||||
name,
|
||||
buildingContext
|
||||
.getBootstrapContext()
|
||||
.getReflectionManager()
|
||||
.toClass( idXProperty.getType() ),
|
||||
generatedValue.generator(),
|
||||
buildingContext.getBuildingOptions().getIdGenerationTypeInterpreter(),
|
||||
interpretGenerationType( generatedValue )
|
||||
);
|
||||
}
|
||||
|
||||
private static GenerationType interpretGenerationType(GeneratedValue generatedValueAnn) {
|
||||
return generatedValueAnn.strategy() == null ? GenerationType.AUTO : generatedValueAnn.strategy();
|
||||
}
|
||||
|
||||
public static Any buildAnyValue(
|
||||
jakarta.persistence.Column discriminatorColumn,
|
||||
Formula discriminatorFormula,
|
||||
|
@ -946,11 +814,8 @@ public class BinderHelper {
|
|||
|
||||
final AnyDiscriminatorValues valuesAnn = property.getAnnotation( AnyDiscriminatorValues.class );
|
||||
if ( valuesAnn != null ) {
|
||||
final AnyDiscriminatorValue[] valueAnns = valuesAnn.value();
|
||||
if ( valueAnns != null && valueAnns.length > 0 ) {
|
||||
for ( AnyDiscriminatorValue ann : valueAnns ) {
|
||||
consumer.accept(ann);
|
||||
}
|
||||
for ( AnyDiscriminatorValue discriminatorValue : valuesAnn.value() ) {
|
||||
consumer.accept( discriminatorValue );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1007,9 +872,9 @@ public class BinderHelper {
|
|||
|
||||
public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
|
||||
final Map<String,String> ret = new HashMap<>();
|
||||
for ( SqlFragmentAlias aliase : aliases ) {
|
||||
if ( isNotEmpty( aliase.table() ) ) {
|
||||
ret.put( aliase.alias(), aliase.table() );
|
||||
for ( SqlFragmentAlias alias : aliases ) {
|
||||
if ( isNotEmpty( alias.table() ) ) {
|
||||
ret.put( alias.alias(), alias.table() );
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
@ -1017,9 +882,9 @@ public class BinderHelper {
|
|||
|
||||
public static Map<String,String> toAliasEntityMap(SqlFragmentAlias[] aliases){
|
||||
final Map<String,String> result = new HashMap<>();
|
||||
for ( SqlFragmentAlias aliase : aliases ) {
|
||||
if ( aliase.entity() != void.class ) {
|
||||
result.put( aliase.alias(), aliase.entity().getName() );
|
||||
for ( SqlFragmentAlias alias : aliases ) {
|
||||
if ( alias.entity() != void.class ) {
|
||||
result.put( alias.alias(), alias.entity().getName() );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -1070,8 +935,9 @@ public class BinderHelper {
|
|||
type.getDeclaredMethod("before").invoke(annotation);
|
||||
final DialectOverride.Version sameOrAfter = (DialectOverride.Version)
|
||||
type.getDeclaredMethod("sameOrAfter").invoke(annotation);
|
||||
if ( dialect.getVersion().isBefore( before.major(), before.minor() )
|
||||
&& dialect.getVersion().isSameOrAfter( sameOrAfter.major(), sameOrAfter.minor() ) ) {
|
||||
DatabaseVersion version = dialect.getVersion();
|
||||
if ( version.isBefore( before.major(), before.minor() )
|
||||
&& version.isSameOrAfter( sameOrAfter.major(), sameOrAfter.minor() ) ) {
|
||||
//noinspection unchecked
|
||||
return (T) type.getDeclaredMethod("override").invoke(annotation);
|
||||
}
|
||||
|
@ -1117,7 +983,7 @@ public class BinderHelper {
|
|||
|
||||
private static EnumSet<CascadeType> convertToHibernateCascadeType(jakarta.persistence.CascadeType[] ejbCascades) {
|
||||
final EnumSet<CascadeType> cascadeTypes = EnumSet.noneOf( CascadeType.class );
|
||||
if ( ejbCascades != null && ejbCascades.length > 0 ) {
|
||||
if ( ejbCascades != null ) {
|
||||
for ( jakarta.persistence.CascadeType cascade: ejbCascades ) {
|
||||
cascadeTypes.add( convertCascadeType( cascade ) );
|
||||
}
|
||||
|
@ -1185,4 +1051,16 @@ public class BinderHelper {
|
|||
return cascade.length() > 0 ? cascade.substring( 1 ) : "none";
|
||||
}
|
||||
|
||||
static boolean isGlobalGeneratorNameGlobal(MetadataBuildingContext context) {
|
||||
return context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled();
|
||||
}
|
||||
|
||||
static boolean isCompositeId(XClass entityClass, XProperty idProperty) {
|
||||
return entityClass.isAnnotationPresent( Embeddable.class )
|
||||
|| idProperty.isAnnotationPresent( EmbeddedId.class );
|
||||
}
|
||||
|
||||
public static boolean isDefault(XClass clazz, MetadataBuildingContext context) {
|
||||
return context.getBootstrapContext().getReflectionManager().equals( clazz, void.class );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,12 +79,10 @@ import org.hibernate.boot.spi.InFlightMetadataCollector.CollectionTypeRegistrati
|
|||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.boot.spi.SecondPass;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.spi.FilterDefinition;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.mapping.Any;
|
||||
import org.hibernate.mapping.Backref;
|
||||
import org.hibernate.mapping.Collection;
|
||||
|
@ -135,6 +133,8 @@ import jakarta.persistence.OrderColumn;
|
|||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
import static jakarta.persistence.AccessType.PROPERTY;
|
||||
import static jakarta.persistence.ConstraintMode.NO_CONSTRAINT;
|
||||
import static jakarta.persistence.ConstraintMode.PROVIDER_DEFAULT;
|
||||
import static jakarta.persistence.FetchType.EAGER;
|
||||
import static jakarta.persistence.FetchType.LAZY;
|
||||
import static org.hibernate.boot.model.internal.AnnotatedClassType.EMBEDDABLE;
|
||||
|
@ -145,27 +145,30 @@ import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnsFrom
|
|||
import static org.hibernate.boot.model.internal.AnnotatedColumn.buildFormulaFromAnnotation;
|
||||
import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinColumnsWithDefaultColumnSuffix;
|
||||
import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinTableJoinColumns;
|
||||
import static org.hibernate.boot.model.internal.AnnotationBinder.fillComponent;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.buildAnyValue;
|
||||
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference;
|
||||
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.BinderHelper.getPath;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isDefault;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isPrimitive;
|
||||
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.EmbeddableBinder.fillEmbeddable;
|
||||
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_ENTITY_WHERE_CLAUSE_FOR_COLLECTIONS;
|
||||
import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.fromResultCheckStyle;
|
||||
import static org.hibernate.internal.util.StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty;
|
||||
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.qualify;
|
||||
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
|
||||
|
||||
/**
|
||||
* Base class for binding different types of collections to mapping model objects
|
||||
* of type {@link Collection}.
|
||||
* Base class for stateful binders responsible for producing mapping model objects of type {@link Collection}.
|
||||
*
|
||||
* @author inger
|
||||
* @author Emmanuel Bernard
|
||||
|
@ -337,7 +340,7 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
if ( property.isAnnotationPresent( CollectionId.class ) ) { //do not compute the generators unless necessary
|
||||
final HashMap<String, IdentifierGeneratorDefinition> localGenerators = new HashMap<>(classGenerators);
|
||||
localGenerators.putAll( AnnotationBinder.buildGenerators( property, context ) );
|
||||
localGenerators.putAll( buildGenerators( property, context ) );
|
||||
collectionBinder.setLocalGenerators( localGenerators );
|
||||
|
||||
}
|
||||
|
@ -1497,7 +1500,7 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
|
||||
XClass getElementType() {
|
||||
if ( AnnotationBinder.isDefault( targetEntity, buildingContext ) ) {
|
||||
if ( isDefault( targetEntity, buildingContext ) ) {
|
||||
if ( collectionElementType != null ) {
|
||||
return collectionElementType;
|
||||
}
|
||||
|
@ -1759,8 +1762,8 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
|
||||
private boolean useEntityWhereClauseForCollections() {
|
||||
return ConfigurationHelper.getBoolean(
|
||||
AvailableSettings.USE_ENTITY_WHERE_CLAUSE_FOR_COLLECTIONS,
|
||||
return getBoolean(
|
||||
USE_ENTITY_WHERE_CLAUSE_FOR_COLLECTIONS,
|
||||
buildingContext
|
||||
.getBuildingOptions()
|
||||
.getServiceRegistry()
|
||||
|
@ -1891,13 +1894,9 @@ public abstract class CollectionBinder {
|
|||
return orderByFragment;
|
||||
}
|
||||
|
||||
private DependantValue buildCollectionKey(
|
||||
Collection collection,
|
||||
AnnotatedJoinColumns joinColumns,
|
||||
OnDeleteAction onDeleteAction,
|
||||
boolean noConstraintByDefault,
|
||||
XProperty property,
|
||||
PropertyHolder propertyHolder) {
|
||||
private DependantValue buildCollectionKey(AnnotatedJoinColumns joinColumns, OnDeleteAction onDeleteAction) {
|
||||
|
||||
final boolean noConstraintByDefault = buildingContext.getBuildingOptions().isNoConstraintByDefault();
|
||||
|
||||
// give a chance to override the referenced property name
|
||||
// has to do that here because the referencedProperty creation happens in a FKSecondPass for ManyToOne yuk!
|
||||
|
@ -1928,8 +1927,8 @@ public abstract class CollectionBinder {
|
|||
final CollectionTable collectionTableAnn = property.getAnnotation( CollectionTable.class );
|
||||
if ( collectionTableAnn != null ) {
|
||||
final ForeignKey foreignKey = collectionTableAnn.foreignKey();
|
||||
if ( foreignKey.value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| foreignKey.value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
if ( foreignKey.value() == NO_CONSTRAINT
|
||||
|| foreignKey.value() == PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
key.disableForeignKey();
|
||||
}
|
||||
else {
|
||||
|
@ -1957,12 +1956,12 @@ public abstract class CollectionBinder {
|
|||
foreignKeyName = joinColumnAnn.foreignKey().name();
|
||||
foreignKeyDefinition = joinColumnAnn.foreignKey().foreignKeyDefinition();
|
||||
}
|
||||
if ( foreignKeyValue != ConstraintMode.NO_CONSTRAINT ) {
|
||||
if ( foreignKeyValue != NO_CONSTRAINT ) {
|
||||
foreignKeyValue = joinColumnAnn.foreignKey().value();
|
||||
}
|
||||
}
|
||||
if ( foreignKeyValue == ConstraintMode.NO_CONSTRAINT
|
||||
|| foreignKeyValue == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
if ( foreignKeyValue == NO_CONSTRAINT
|
||||
|| foreignKeyValue == PROVIDER_DEFAULT && noConstraintByDefault ) {
|
||||
key.disableForeignKey();
|
||||
}
|
||||
else {
|
||||
|
@ -2003,8 +2002,8 @@ public abstract class CollectionBinder {
|
|||
|
||||
private static void handleForeignKeyConstraint(boolean noConstraintByDefault, DependantValue key, ForeignKey foreignKey) {
|
||||
final ConstraintMode constraintMode = foreignKey.value();
|
||||
if ( constraintMode == ConstraintMode.NO_CONSTRAINT
|
||||
|| constraintMode == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault) {
|
||||
if ( constraintMode == NO_CONSTRAINT
|
||||
|| constraintMode == PROVIDER_DEFAULT && noConstraintByDefault) {
|
||||
key.disableForeignKey();
|
||||
}
|
||||
else {
|
||||
|
@ -2195,7 +2194,7 @@ public abstract class CollectionBinder {
|
|||
CollectionPropertyHolder holder,
|
||||
Class<? extends CompositeUserType<?>> compositeUserType) {
|
||||
//TODO be smart with isNullable
|
||||
final Component component = fillComponent(
|
||||
final Component component = fillEmbeddable(
|
||||
holder,
|
||||
getSpecialMembers( elementClass ),
|
||||
accessType( property, collection.getOwner() ),
|
||||
|
@ -2303,7 +2302,7 @@ public abstract class CollectionBinder {
|
|||
element.setReferencedEntityName( elementType.getName() );
|
||||
//element.setFetchMode( fetchMode );
|
||||
//element.setLazy( fetchMode != FetchMode.JOIN );
|
||||
//make the second join non lazy
|
||||
//make the second join non-lazy
|
||||
element.setFetchMode( FetchMode.JOIN );
|
||||
element.setLazy( false );
|
||||
element.setNotFoundAction( notFoundAction );
|
||||
|
@ -2328,8 +2327,8 @@ public abstract class CollectionBinder {
|
|||
foreignKeyDefinition = joinColumnAnn.foreignKey().foreignKeyDefinition();
|
||||
}
|
||||
}
|
||||
if ( joinTableAnn.inverseForeignKey().value() == ConstraintMode.NO_CONSTRAINT
|
||||
|| joinTableAnn.inverseForeignKey().value() == ConstraintMode.PROVIDER_DEFAULT
|
||||
if ( joinTableAnn.inverseForeignKey().value() == NO_CONSTRAINT
|
||||
|| joinTableAnn.inverseForeignKey().value() == PROVIDER_DEFAULT
|
||||
&& buildingContext.getBuildingOptions().isNoConstraintByDefault() ) {
|
||||
element.disableForeignKey();
|
||||
}
|
||||
|
@ -2582,19 +2581,11 @@ public abstract class CollectionBinder {
|
|||
);
|
||||
}
|
||||
|
||||
final DependantValue key = buildCollectionKey(
|
||||
collection,
|
||||
joinColumns,
|
||||
onDeleteAction,
|
||||
buildingContext.getBuildingOptions().isNoConstraintByDefault(),
|
||||
property,
|
||||
propertyHolder
|
||||
);
|
||||
|
||||
if ( property.isAnnotationPresent( ElementCollection.class ) ) {
|
||||
joinColumns.setElementCollection( true );
|
||||
}
|
||||
|
||||
final DependantValue key = buildCollectionKey( joinColumns, onDeleteAction );
|
||||
TableBinder.bindForeignKey(
|
||||
collection.getOwner(),
|
||||
targetEntity,
|
||||
|
|
|
@ -0,0 +1,610 @@
|
|||
/*
|
||||
* 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 jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.annotations.Instantiator;
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XMethod;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.spi.AccessType;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyCompositeUserTypeImpl;
|
||||
import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
|
||||
import org.hibernate.property.access.spi.PropertyAccessStrategy;
|
||||
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
|
||||
import org.hibernate.usertype.CompositeUserType;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
|
||||
import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass;
|
||||
import static org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations;
|
||||
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
|
||||
import static org.hibernate.boot.model.internal.GeneratorBinder.generatorType;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getPath;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getRelativePath;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
|
||||
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
|
||||
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
|
||||
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
|
||||
|
||||
/**
|
||||
* A binder responsible for interpreting {@link Embeddable} classes and producing
|
||||
* instances of the mapping model object {@link Component}.
|
||||
*/
|
||||
public class EmbeddableBinder {
|
||||
private static final CoreMessageLogger LOG = messageLogger( EmbeddableBinder.class );
|
||||
|
||||
static PropertyBinder createCompositeBinder(
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData inferredData,
|
||||
EntityBinder entityBinder,
|
||||
boolean isIdentifierMapper,
|
||||
boolean isComponentEmbedded,
|
||||
MetadataBuildingContext context,
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass,
|
||||
XProperty property,
|
||||
AnnotatedColumns columns,
|
||||
XClass returnedClass,
|
||||
PropertyBinder propertyBinder,
|
||||
boolean isOverridden,
|
||||
Class<? extends CompositeUserType<?>> compositeUserType) {
|
||||
final String referencedEntityName;
|
||||
final String propertyName;
|
||||
final AnnotatedJoinColumns actualColumns;
|
||||
if ( isOverridden ) {
|
||||
// careful: not always a @MapsId property, sometimes it's from an @IdClass
|
||||
final PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId(
|
||||
propertyBinder.isId(),
|
||||
propertyHolder,
|
||||
property.getName(),
|
||||
context
|
||||
);
|
||||
referencedEntityName = mapsIdProperty.getClassOrElementName();
|
||||
propertyName = mapsIdProperty.getPropertyName();
|
||||
final AnnotatedJoinColumns parent = new AnnotatedJoinColumns();
|
||||
parent.setBuildingContext( context );
|
||||
parent.setPropertyHolder( propertyHolder );
|
||||
parent.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
|
||||
//TODO: resetting the parent here looks like a dangerous thing to do
|
||||
// should we be cloning them first (the legacy code did not)
|
||||
for ( AnnotatedColumn column : columns.getColumns() ) {
|
||||
column.setParent( parent );
|
||||
}
|
||||
actualColumns = parent;
|
||||
}
|
||||
else {
|
||||
referencedEntityName = null;
|
||||
propertyName = null;
|
||||
actualColumns = null;
|
||||
}
|
||||
|
||||
return bindEmbeddable(
|
||||
inferredData,
|
||||
propertyHolder,
|
||||
entityBinder.getPropertyAccessor( property ),
|
||||
entityBinder,
|
||||
isIdentifierMapper,
|
||||
context,
|
||||
isComponentEmbedded,
|
||||
propertyBinder.isId(),
|
||||
inheritanceStatePerClass,
|
||||
referencedEntityName,
|
||||
propertyName,
|
||||
determineCustomInstantiator( property, returnedClass, context ),
|
||||
compositeUserType,
|
||||
actualColumns,
|
||||
columns
|
||||
);
|
||||
}
|
||||
|
||||
static boolean isEmbedded(XProperty property, XClass returnedClass) {
|
||||
return property.isAnnotationPresent( Embedded.class )
|
||||
|| property.isAnnotationPresent( EmbeddedId.class )
|
||||
|| returnedClass.isAnnotationPresent( Embeddable.class );
|
||||
}
|
||||
|
||||
private static PropertyBinder bindEmbeddable(
|
||||
PropertyData inferredData,
|
||||
PropertyHolder propertyHolder,
|
||||
AccessType propertyAccessor,
|
||||
EntityBinder entityBinder,
|
||||
boolean isIdentifierMapper,
|
||||
MetadataBuildingContext context,
|
||||
boolean isComponentEmbedded,
|
||||
boolean isId, //is an identifier
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass,
|
||||
String referencedEntityName, //is a component who is overridden by a @MapsId
|
||||
String propertyName,
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
|
||||
AnnotatedJoinColumns columns,
|
||||
AnnotatedColumns annotatedColumns) {
|
||||
final Component component;
|
||||
if ( referencedEntityName != null ) {
|
||||
component = createEmbeddable(
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
isComponentEmbedded,
|
||||
isIdentifierMapper,
|
||||
customInstantiatorImpl,
|
||||
context
|
||||
);
|
||||
context.getMetadataCollector().addSecondPass( new CopyIdentifierComponentSecondPass(
|
||||
component,
|
||||
referencedEntityName,
|
||||
propertyName,
|
||||
columns,
|
||||
context
|
||||
) );
|
||||
}
|
||||
else {
|
||||
component = fillEmbeddable(
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
propertyAccessor,
|
||||
!isId,
|
||||
entityBinder,
|
||||
isComponentEmbedded,
|
||||
isIdentifierMapper,
|
||||
false,
|
||||
customInstantiatorImpl,
|
||||
compositeUserTypeClass,
|
||||
annotatedColumns,
|
||||
context,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
}
|
||||
if ( isId ) {
|
||||
component.setKey( true );
|
||||
checkEmbeddedId( inferredData, propertyHolder, referencedEntityName, component );
|
||||
}
|
||||
final PropertyBinder binder = new PropertyBinder();
|
||||
binder.setDeclaringClass( inferredData.getDeclaringClass() );
|
||||
binder.setName( inferredData.getPropertyName() );
|
||||
binder.setValue( component );
|
||||
binder.setProperty( inferredData.getProperty() );
|
||||
binder.setAccessType( inferredData.getDefaultAccess() );
|
||||
binder.setEmbedded( isComponentEmbedded );
|
||||
binder.setHolder( propertyHolder );
|
||||
binder.setId( isId );
|
||||
binder.setEntityBinder( entityBinder );
|
||||
binder.setInheritanceStatePerClass( inheritanceStatePerClass );
|
||||
binder.setBuildingContext( context );
|
||||
binder.makePropertyAndBind();
|
||||
return binder;
|
||||
}
|
||||
|
||||
private static void checkEmbeddedId(
|
||||
PropertyData inferredData,
|
||||
PropertyHolder propertyHolder,
|
||||
String referencedEntityName,
|
||||
Component component) {
|
||||
if ( propertyHolder.getPersistentClass().getIdentifier() != null ) {
|
||||
throw new AnnotationException(
|
||||
"Embeddable class '" + component.getComponentClassName()
|
||||
+ "' may not have a property annotated '@Id' since it is used by '"
|
||||
+ getPath(propertyHolder, inferredData)
|
||||
+ "' as an '@EmbeddedId'"
|
||||
);
|
||||
}
|
||||
if ( referencedEntityName == null && component.getPropertySpan() == 0 ) {
|
||||
throw new AnnotationException(
|
||||
"Embeddable class '" + component.getComponentClassName()
|
||||
+ "' may not be used as an '@EmbeddedId' by '"
|
||||
+ getPath(propertyHolder, inferredData)
|
||||
+ "' because it has no properties"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static Component fillEmbeddable(
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData inferredData,
|
||||
AccessType propertyAccessor,
|
||||
boolean isNullable,
|
||||
EntityBinder entityBinder,
|
||||
boolean isComponentEmbedded,
|
||||
boolean isIdentifierMapper,
|
||||
boolean inSecondPass,
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
|
||||
AnnotatedColumns columns,
|
||||
MetadataBuildingContext context,
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass) {
|
||||
return fillEmbeddable(
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
null,
|
||||
propertyAccessor,
|
||||
isNullable,
|
||||
entityBinder,
|
||||
isComponentEmbedded,
|
||||
isIdentifierMapper,
|
||||
inSecondPass,
|
||||
customInstantiatorImpl,
|
||||
compositeUserTypeClass,
|
||||
columns,
|
||||
context,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
}
|
||||
|
||||
static Component fillEmbeddable(
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData inferredData,
|
||||
PropertyData baseInferredData, //base inferred data correspond to the entity reproducing inferredData's properties (ie IdClass)
|
||||
AccessType propertyAccessor,
|
||||
boolean isNullable,
|
||||
EntityBinder entityBinder,
|
||||
boolean isComponentEmbedded,
|
||||
boolean isIdentifierMapper,
|
||||
boolean inSecondPass,
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
|
||||
AnnotatedColumns columns,
|
||||
MetadataBuildingContext context,
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass) {
|
||||
// inSecondPass can only be used to apply right away the second pass of a composite-element
|
||||
// Because it's a value type, there is no bidirectional association, hence second pass
|
||||
// ordering does not matter
|
||||
final Component component = createEmbeddable(
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
isComponentEmbedded,
|
||||
isIdentifierMapper,
|
||||
customInstantiatorImpl,
|
||||
context
|
||||
);
|
||||
|
||||
final String subpath = getPath( propertyHolder, inferredData );
|
||||
LOG.tracev( "Binding component with path: {0}", subpath );
|
||||
final PropertyHolder subholder = buildPropertyHolder(
|
||||
component,
|
||||
subpath,
|
||||
inferredData,
|
||||
propertyHolder,
|
||||
context
|
||||
);
|
||||
|
||||
// propertyHolder here is the owner of the component property.
|
||||
// Tell it we are about to start the component...
|
||||
propertyHolder.startingProperty( inferredData.getProperty() );
|
||||
|
||||
final CompositeUserType<?> compositeUserType;
|
||||
final XClass returnedClassOrElement;
|
||||
if ( compositeUserTypeClass == null ) {
|
||||
compositeUserType = null;
|
||||
returnedClassOrElement = inferredData.getClassOrElement();
|
||||
}
|
||||
else {
|
||||
compositeUserType = compositeUserType( compositeUserTypeClass, context );
|
||||
component.setTypeName( compositeUserTypeClass.getName() );
|
||||
returnedClassOrElement = context.getBootstrapContext().getReflectionManager()
|
||||
.toXClass( compositeUserType.embeddable() );
|
||||
}
|
||||
|
||||
final XClass annotatedClass = inferredData.getPropertyClass();
|
||||
final List<PropertyData> classElements =
|
||||
collectClassElements( propertyAccessor, context, returnedClassOrElement, annotatedClass );
|
||||
final List<PropertyData> baseClassElements =
|
||||
collectBaseClassElements( baseInferredData, propertyAccessor, context, annotatedClass );
|
||||
if ( baseClassElements != null
|
||||
//useful to avoid breaking pre JPA 2 mappings
|
||||
&& !hasAnnotationsOnIdClass( annotatedClass ) ) {
|
||||
processIdClassElememts( propertyHolder, baseInferredData, classElements, baseClassElements );
|
||||
}
|
||||
for ( PropertyData propertyAnnotatedElement : classElements ) {
|
||||
processElementAnnotations(
|
||||
subholder,
|
||||
isNullable ? Nullability.NO_CONSTRAINT : Nullability.FORCED_NOT_NULL,
|
||||
propertyAnnotatedElement,
|
||||
new HashMap<>(),
|
||||
entityBinder,
|
||||
isIdentifierMapper,
|
||||
isComponentEmbedded,
|
||||
inSecondPass,
|
||||
context,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
|
||||
final XProperty property = propertyAnnotatedElement.getProperty();
|
||||
if ( isGeneratedId( property ) ) {
|
||||
processGeneratedId( context, component, property );
|
||||
}
|
||||
}
|
||||
|
||||
if ( compositeUserType != null ) {
|
||||
processCompositeUserType( component, compositeUserType );
|
||||
}
|
||||
AggregateComponentBinder.processAggregate(
|
||||
component,
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
returnedClassOrElement,
|
||||
columns,
|
||||
context
|
||||
);
|
||||
return component;
|
||||
}
|
||||
|
||||
private static CompositeUserType<?> compositeUserType(
|
||||
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
|
||||
MetadataBuildingContext context) {
|
||||
return context.getBootstrapContext().getServiceRegistry()
|
||||
.getService( ManagedBeanRegistry.class )
|
||||
.getBean( compositeUserTypeClass )
|
||||
.getBeanInstance();
|
||||
}
|
||||
|
||||
private static List<PropertyData> collectClassElements(
|
||||
AccessType propertyAccessor,
|
||||
MetadataBuildingContext context,
|
||||
XClass returnedClassOrElement,
|
||||
XClass annotatedClass) {
|
||||
final List<PropertyData> classElements = new ArrayList<>();
|
||||
//embeddable elements can have type defs
|
||||
final PropertyContainer container =
|
||||
new PropertyContainer( returnedClassOrElement, annotatedClass, propertyAccessor );
|
||||
addElementsOfClass( classElements, container, context);
|
||||
//add elements of the embeddable's mapped superclasses
|
||||
XClass superClass = annotatedClass.getSuperclass();
|
||||
while ( superClass != null && superClass.isAnnotationPresent( MappedSuperclass.class ) ) {
|
||||
//FIXME: proper support of type variables incl var resolved at upper levels
|
||||
final PropertyContainer superContainer =
|
||||
new PropertyContainer( superClass, annotatedClass, propertyAccessor );
|
||||
addElementsOfClass( classElements, superContainer, context );
|
||||
superClass = superClass.getSuperclass();
|
||||
}
|
||||
return classElements;
|
||||
}
|
||||
|
||||
private static List<PropertyData> collectBaseClassElements(
|
||||
PropertyData baseInferredData,
|
||||
AccessType propertyAccessor,
|
||||
MetadataBuildingContext context,
|
||||
XClass annotatedClass) {
|
||||
if ( baseInferredData != null ) {
|
||||
final List<PropertyData> baseClassElements = new ArrayList<>();
|
||||
// iterate from base returned class up hierarchy to handle cases where the @Id attributes
|
||||
// might be spread across the subclasses and super classes.
|
||||
XClass baseReturnedClassOrElement = baseInferredData.getClassOrElement();
|
||||
while ( !Object.class.getName().equals( baseReturnedClassOrElement.getName() ) ) {
|
||||
final PropertyContainer container =
|
||||
new PropertyContainer( baseReturnedClassOrElement, annotatedClass, propertyAccessor );
|
||||
addElementsOfClass( baseClassElements, container, context );
|
||||
baseReturnedClassOrElement = baseReturnedClassOrElement.getSuperclass();
|
||||
}
|
||||
return baseClassElements;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isGeneratedId(XProperty property) {
|
||||
return property.isAnnotationPresent( GeneratedValue.class )
|
||||
&& property.isAnnotationPresent( Id.class );
|
||||
}
|
||||
|
||||
private static void processCompositeUserType(Component component, CompositeUserType<?> compositeUserType) {
|
||||
component.sortProperties();
|
||||
final List<String> sortedPropertyNames = new ArrayList<>( component.getPropertySpan() );
|
||||
final List<Type> sortedPropertyTypes = new ArrayList<>( component.getPropertySpan() );
|
||||
final PropertyAccessStrategy strategy = new PropertyAccessStrategyCompositeUserTypeImpl(
|
||||
compositeUserType,
|
||||
sortedPropertyNames,
|
||||
sortedPropertyTypes
|
||||
);
|
||||
for ( Property property : component.getProperties() ) {
|
||||
sortedPropertyNames.add( property.getName() );
|
||||
sortedPropertyTypes.add(
|
||||
PropertyAccessStrategyMixedImpl.INSTANCE.buildPropertyAccess(
|
||||
compositeUserType.embeddable(),
|
||||
property.getName(),
|
||||
false
|
||||
).getGetter().getReturnType()
|
||||
);
|
||||
property.setPropertyAccessStrategy( strategy );
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasAnnotationsOnIdClass(XClass idClass) {
|
||||
for ( XProperty property : idClass.getDeclaredProperties( XClass.ACCESS_FIELD ) ) {
|
||||
if ( hasTriggeringAnnotation( property ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for ( XMethod method : idClass.getDeclaredMethods() ) {
|
||||
if ( hasTriggeringAnnotation( method ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasTriggeringAnnotation(XAnnotatedElement property) {
|
||||
return property.isAnnotationPresent(Column.class)
|
||||
|| property.isAnnotationPresent(OneToMany.class)
|
||||
|| property.isAnnotationPresent(ManyToOne.class)
|
||||
|| property.isAnnotationPresent(Id.class)
|
||||
|| property.isAnnotationPresent(GeneratedValue.class)
|
||||
|| property.isAnnotationPresent(OneToOne.class)
|
||||
|| property.isAnnotationPresent(ManyToMany.class);
|
||||
}
|
||||
|
||||
private static void processGeneratedId(MetadataBuildingContext context, Component component, XProperty property) {
|
||||
final GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
|
||||
final String generatorType = generatedValue != null
|
||||
? generatorType( generatedValue, property.getType(), context )
|
||||
: DEFAULT_ID_GEN_STRATEGY;
|
||||
final String generator = generatedValue != null ? generatedValue.generator() : "";
|
||||
|
||||
if ( isGlobalGeneratorNameGlobal( context ) ) {
|
||||
buildGenerators( property, context );
|
||||
context.getMetadataCollector().addSecondPass( new IdGeneratorResolverSecondPass(
|
||||
(SimpleValue) component.getProperty( property.getName() ).getValue(),
|
||||
property,
|
||||
generatorType,
|
||||
generator,
|
||||
context
|
||||
) );
|
||||
|
||||
// handleTypeDescriptorRegistrations( property, context );
|
||||
// bindEmbeddableInstantiatorRegistrations( property, context );
|
||||
// bindCompositeUserTypeRegistrations( property, context );
|
||||
// handleConverterRegistrations( property, context );
|
||||
}
|
||||
else {
|
||||
makeIdGenerator(
|
||||
(SimpleValue) component.getProperty( property.getName() ).getValue(),
|
||||
property,
|
||||
generatorType,
|
||||
generator,
|
||||
context,
|
||||
new HashMap<>( buildGenerators( property, context ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processIdClassElememts(
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData baseInferredData,
|
||||
List<PropertyData> classElements,
|
||||
List<PropertyData> baseClassElements) {
|
||||
final Map<String, PropertyData> baseClassElementsByName = new HashMap<>();
|
||||
for ( PropertyData element : baseClassElements ) {
|
||||
baseClassElementsByName.put( element.getPropertyName(), element );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < classElements.size(); i++ ) {
|
||||
final PropertyData idClassPropertyData = classElements.get( i );
|
||||
final PropertyData entityPropertyData =
|
||||
baseClassElementsByName.get( idClassPropertyData.getPropertyName() );
|
||||
if ( propertyHolder.isInIdClass() ) {
|
||||
if ( entityPropertyData == null ) {
|
||||
throw new AnnotationException(
|
||||
"Property '" + getPath(propertyHolder, idClassPropertyData )
|
||||
+ "' belongs to an '@IdClass' but has no matching property in entity class '"
|
||||
+ baseInferredData.getPropertyClass().getName()
|
||||
+ "' (every property of the '@IdClass' must have a corresponding persistent property in the '@Entity' class)"
|
||||
);
|
||||
}
|
||||
if ( hasToOneAnnotation( entityPropertyData.getProperty() )
|
||||
&& !entityPropertyData.getClassOrElement().equals( idClassPropertyData.getClassOrElement() ) ) {
|
||||
//don't replace here as we need to use the actual original return type
|
||||
//the annotation overriding will be dealt with by a mechanism similar to @MapsId
|
||||
continue;
|
||||
}
|
||||
}
|
||||
classElements.set( i, entityPropertyData ); //this works since they are in the same order
|
||||
}
|
||||
}
|
||||
|
||||
static Component createEmbeddable(
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData inferredData,
|
||||
boolean isComponentEmbedded,
|
||||
boolean isIdentifierMapper,
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
MetadataBuildingContext context) {
|
||||
final Component component = new Component( context, propertyHolder.getPersistentClass() );
|
||||
component.setEmbedded( isComponentEmbedded );
|
||||
//yuk
|
||||
component.setTable( propertyHolder.getTable() );
|
||||
//FIXME shouldn't identifier mapper use getClassOrElementName? Need to be checked.
|
||||
if ( isIdentifierMapper
|
||||
|| isComponentEmbedded && inferredData.getPropertyName() == null ) {
|
||||
component.setComponentClassName( component.getOwner().getClassName() );
|
||||
}
|
||||
else {
|
||||
component.setComponentClassName( inferredData.getClassOrElementName() );
|
||||
}
|
||||
component.setCustomInstantiator( customInstantiatorImpl );
|
||||
final Constructor<?> constructor = resolveInstantiator( inferredData.getClassOrElement(), context );
|
||||
if ( constructor != null ) {
|
||||
component.setInstantiator( constructor, constructor.getAnnotation( Instantiator.class ).value() );
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
private static Constructor<?> resolveInstantiator(XClass embeddableClass, MetadataBuildingContext buildingContext) {
|
||||
if ( embeddableClass != null ) {
|
||||
final Constructor<?>[] declaredConstructors = buildingContext.getBootstrapContext().getReflectionManager()
|
||||
.toClass( embeddableClass )
|
||||
.getDeclaredConstructors();
|
||||
Constructor<?> constructor = null;
|
||||
for ( Constructor<?> declaredConstructor : declaredConstructors ) {
|
||||
if ( declaredConstructor.isAnnotationPresent( Instantiator.class ) ) {
|
||||
if ( constructor != null ) {
|
||||
throw new AnnotationException( "Multiple constructors of '" + embeddableClass.getName()
|
||||
+ "' are annotated '@Instantiator' but only one constructor can be the canonical constructor" );
|
||||
}
|
||||
constructor = declaredConstructor;
|
||||
}
|
||||
}
|
||||
return constructor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class<? extends EmbeddableInstantiator> determineCustomInstantiator(
|
||||
XProperty property,
|
||||
XClass returnedClass,
|
||||
MetadataBuildingContext context) {
|
||||
if ( property.isAnnotationPresent( EmbeddedId.class ) ) {
|
||||
// we don't allow custom instantiators for composite ids
|
||||
return null;
|
||||
}
|
||||
|
||||
final org.hibernate.annotations.EmbeddableInstantiator propertyAnnotation =
|
||||
property.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
|
||||
if ( propertyAnnotation != null ) {
|
||||
return propertyAnnotation.value();
|
||||
}
|
||||
|
||||
final org.hibernate.annotations.EmbeddableInstantiator classAnnotation =
|
||||
returnedClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
|
||||
if ( classAnnotation != null ) {
|
||||
return classAnnotation.value();
|
||||
}
|
||||
|
||||
final Class<?> embeddableClass = context.getBootstrapContext().getReflectionManager().toClass( returnedClass );
|
||||
if ( embeddableClass != null ) {
|
||||
return context.getMetadataCollector().findRegisteredEmbeddableInstantiator( embeddableClass );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,27 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.AttributeOverrides;
|
||||
import jakarta.persistence.Cacheable;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.DiscriminatorColumn;
|
||||
import jakarta.persistence.DiscriminatorValue;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.Inheritance;
|
||||
import jakarta.persistence.InheritanceType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.NamedEntityGraph;
|
||||
import jakarta.persistence.NamedEntityGraphs;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumns;
|
||||
import jakarta.persistence.SecondaryTable;
|
||||
import jakarta.persistence.SecondaryTables;
|
||||
import jakarta.persistence.SharedCacheMode;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -99,37 +120,20 @@ import org.hibernate.spi.NavigablePath;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import jakarta.persistence.Access;
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.AttributeOverrides;
|
||||
import jakarta.persistence.Cacheable;
|
||||
import jakarta.persistence.ConstraintMode;
|
||||
import jakarta.persistence.DiscriminatorColumn;
|
||||
import jakarta.persistence.DiscriminatorValue;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.InheritanceType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.NamedEntityGraph;
|
||||
import jakarta.persistence.NamedEntityGraphs;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumns;
|
||||
import jakarta.persistence.SecondaryTable;
|
||||
import jakarta.persistence.SecondaryTables;
|
||||
import jakarta.persistence.SharedCacheMode;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
import static org.hibernate.boot.model.internal.AnnotatedClassType.MAPPED_SUPERCLASS;
|
||||
import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.buildDiscriminatorColumn;
|
||||
import static org.hibernate.boot.model.internal.AnnotatedJoinColumn.buildInheritanceJoinColumn;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getMappedSuperclassOrNull;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.makeIdGenerator;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isDefault;
|
||||
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
|
||||
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.EmbeddableBinder.fillEmbeddable;
|
||||
import static org.hibernate.boot.model.internal.InheritanceState.getInheritanceStateOfSuperEntity;
|
||||
import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass;
|
||||
import static org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations;
|
||||
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
|
||||
import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.fromResultCheckStyle;
|
||||
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||
|
@ -142,7 +146,8 @@ import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
|
|||
|
||||
|
||||
/**
|
||||
* Stateful holder and processor for binding information about an {@link Entity} class.
|
||||
* Stateful binder responsible for interpreting information about an {@link Entity} class
|
||||
* and producing a {@link PersistentClass} mapping model object.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
|
@ -400,7 +405,7 @@ public class EntityBinder {
|
|||
XClass compositeClass,
|
||||
PropertyData baseInferredData,
|
||||
AccessType propertyAccessor) {
|
||||
final Component mapper = AnnotationBinder.fillComponent(
|
||||
final Component mapper = fillEmbeddable(
|
||||
propertyHolder,
|
||||
new PropertyPreloadedData(
|
||||
propertyAccessor,
|
||||
|
@ -434,6 +439,22 @@ public class EntityBinder {
|
|||
return mapper;
|
||||
}
|
||||
|
||||
private static PropertyData getUniqueIdPropertyFromBaseClass(
|
||||
PropertyData inferredData,
|
||||
PropertyData baseInferredData,
|
||||
AccessType propertyAccessor,
|
||||
MetadataBuildingContext context) {
|
||||
final List<PropertyData> baseClassElements = new ArrayList<>();
|
||||
final PropertyContainer propContainer = new PropertyContainer(
|
||||
baseInferredData.getClassOrElement(),
|
||||
inferredData.getPropertyClass(),
|
||||
propertyAccessor
|
||||
);
|
||||
addElementsOfClass( baseClassElements, propContainer, context );
|
||||
//Id properties are on top and there is only one
|
||||
return baseClassElements.get( 0 );
|
||||
}
|
||||
|
||||
private static boolean isIdClassPkOfTheAssociatedEntity(
|
||||
ElementsToProcess elementsToProcess,
|
||||
XClass compositeClass,
|
||||
|
@ -443,7 +464,7 @@ public class EntityBinder {
|
|||
Map<XClass, InheritanceState> inheritanceStates,
|
||||
MetadataBuildingContext context) {
|
||||
if ( elementsToProcess.getIdPropertyCount() == 1 ) {
|
||||
final PropertyData idPropertyOnBaseClass = AnnotationBinder.getUniqueIdPropertyFromBaseClass(
|
||||
final PropertyData idPropertyOnBaseClass = getUniqueIdPropertyFromBaseClass(
|
||||
inferredData,
|
||||
baseInferredData,
|
||||
propertyAccessor,
|
||||
|
@ -488,7 +509,7 @@ public class EntityBinder {
|
|||
+ "' 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 = AnnotationBinder.fillComponent(
|
||||
final Component id = fillEmbeddable(
|
||||
propertyHolder,
|
||||
inferredData,
|
||||
baseInferredData,
|
||||
|
@ -783,7 +804,7 @@ public class EntityBinder {
|
|||
final DiscriminatorFormula discriminatorFormula =
|
||||
getOverridableAnnotation( annotatedClass, DiscriminatorFormula.class, context );
|
||||
|
||||
if ( !inheritanceState.hasParents() ) {
|
||||
if ( !inheritanceState.hasParents() || annotatedClass.isAnnotationPresent( Inheritance.class ) ) {
|
||||
return buildDiscriminatorColumn( discriminatorColumn, discriminatorFormula, context );
|
||||
}
|
||||
else {
|
||||
|
@ -812,7 +833,7 @@ public class EntityBinder {
|
|||
}
|
||||
|
||||
final DiscriminatorColumn discriminatorColumn = annotatedClass.getAnnotation( DiscriminatorColumn.class );
|
||||
if ( !inheritanceState.hasParents() ) {
|
||||
if ( !inheritanceState.hasParents() || annotatedClass.isAnnotationPresent( Inheritance.class ) ) {
|
||||
return useDiscriminatorColumnForJoined( discriminatorColumn )
|
||||
? buildDiscriminatorColumn( discriminatorColumn, null, context )
|
||||
: null;
|
||||
|
@ -876,7 +897,7 @@ public class EntityBinder {
|
|||
boolean subclassAndSingleTableStrategy =
|
||||
inheritanceState.getType() == InheritanceType.SINGLE_TABLE
|
||||
&& inheritanceState.hasParents();
|
||||
AnnotationBinder.processElementAnnotations(
|
||||
processElementAnnotations(
|
||||
propertyHolder,
|
||||
subclassAndSingleTableStrategy
|
||||
? Nullability.FORCED_NULL
|
||||
|
@ -1355,7 +1376,7 @@ public class EntityBinder {
|
|||
}
|
||||
else {
|
||||
final ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();
|
||||
proxyClass = AnnotationBinder.isDefault( reflectionManager.toXClass(proxy.proxyClass() ), context )
|
||||
proxyClass = isDefault( reflectionManager.toXClass(proxy.proxyClass() ), context )
|
||||
? annotatedClass
|
||||
: reflectionManager.toXClass(proxy.proxyClass());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* 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 jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import jakarta.persistence.SequenceGenerators;
|
||||
import jakarta.persistence.TableGenerator;
|
||||
import jakarta.persistence.TableGenerators;
|
||||
import jakarta.persistence.Version;
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.IdGeneratorType;
|
||||
import org.hibernate.annotations.Parameter;
|
||||
import org.hibernate.annotations.ValueGenerationType;
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.model.IdGeneratorStrategyInterpreter;
|
||||
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
|
||||
import org.hibernate.boot.model.relational.ExportableProducer;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.generator.AnnotationBasedGenerator;
|
||||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.GeneratorCreationContext;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.id.PersistentIdentifierGenerator;
|
||||
import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.mapping.GeneratorCreator;
|
||||
import org.hibernate.mapping.IdentifierGeneratorCreator;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isCompositeId;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
|
||||
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
|
||||
|
||||
public class GeneratorBinder {
|
||||
|
||||
private static final Logger LOG = CoreLogging.logger( BinderHelper.class );
|
||||
|
||||
/**
|
||||
* Apply an id generation strategy and parameters to the
|
||||
* given {@link SimpleValue} which represents an identifier.
|
||||
*/
|
||||
public static void makeIdGenerator(
|
||||
SimpleValue id,
|
||||
XProperty property,
|
||||
String generatorType,
|
||||
String generatorName,
|
||||
MetadataBuildingContext buildingContext,
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators) {
|
||||
LOG.debugf( "#makeIdGenerator(%s, %s, %s, %s, ...)", id, property, generatorType, generatorName );
|
||||
|
||||
final Table table = id.getTable();
|
||||
table.setIdentifierValue( id );
|
||||
//generator settings
|
||||
id.setIdentifierGeneratorStrategy( generatorType );
|
||||
|
||||
final Map<String,Object> parameters = new HashMap<>();
|
||||
|
||||
//always settable
|
||||
parameters.put( PersistentIdentifierGenerator.TABLE, table.getName() );
|
||||
|
||||
if ( id.getColumnSpan() == 1 ) {
|
||||
parameters.put( PersistentIdentifierGenerator.PK, id.getColumns().get(0).getName() );
|
||||
}
|
||||
// YUCK! but cannot think of a clean way to do this given the string-config based scheme
|
||||
parameters.put( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, buildingContext.getObjectNameNormalizer() );
|
||||
parameters.put( IdentifierGenerator.GENERATOR_NAME, generatorName );
|
||||
|
||||
if ( !generatorName.isEmpty() ) {
|
||||
//we have a named generator
|
||||
final IdentifierGeneratorDefinition definition = makeIdentifierGeneratorDefinition(
|
||||
generatorName,
|
||||
property,
|
||||
localGenerators,
|
||||
buildingContext
|
||||
);
|
||||
if ( definition == null ) {
|
||||
throw new AnnotationException( "No id generator was declared with the name '" + generatorName
|
||||
+ "' specified by '@GeneratedValue'"
|
||||
+ " (define a named generator using '@SequenceGenerator', '@TableGenerator', or '@GenericGenerator')" );
|
||||
}
|
||||
//This is quite vague in the spec but a generator could override the generator choice
|
||||
final String identifierGeneratorStrategy = definition.getStrategy();
|
||||
//yuk! this is a hack not to override 'AUTO' even if generator is set
|
||||
final boolean avoidOverriding = identifierGeneratorStrategy.equals( "identity" )
|
||||
|| identifierGeneratorStrategy.equals( "seqhilo" );
|
||||
if ( generatorType == null || !avoidOverriding ) {
|
||||
id.setIdentifierGeneratorStrategy( identifierGeneratorStrategy );
|
||||
if ( identifierGeneratorStrategy.equals( "assigned" ) ) {
|
||||
id.setNullValue( "undefined" );
|
||||
}
|
||||
}
|
||||
//checkIfMatchingGenerator(definition, generatorType, generatorName);
|
||||
parameters.putAll( definition.getParameters() );
|
||||
}
|
||||
if ( DEFAULT_ID_GEN_STRATEGY.equals( generatorType ) ) {
|
||||
id.setNullValue( "undefined" );
|
||||
}
|
||||
id.setIdentifierGeneratorParameters( parameters );
|
||||
}
|
||||
|
||||
/**
|
||||
* apply an id generator to a SimpleValue
|
||||
*/
|
||||
public static void makeIdGenerator(
|
||||
SimpleValue id,
|
||||
XProperty idXProperty,
|
||||
String generatorType,
|
||||
String generatorName,
|
||||
MetadataBuildingContext buildingContext,
|
||||
IdentifierGeneratorDefinition foreignKGeneratorDefinition) {
|
||||
Map<String, IdentifierGeneratorDefinition> localIdentifiers = null;
|
||||
if ( foreignKGeneratorDefinition != null ) {
|
||||
localIdentifiers = new HashMap<>();
|
||||
localIdentifiers.put( foreignKGeneratorDefinition.getName(), foreignKGeneratorDefinition );
|
||||
}
|
||||
makeIdGenerator( id, idXProperty, generatorType, generatorName, buildingContext, localIdentifiers );
|
||||
}
|
||||
|
||||
private static IdentifierGeneratorDefinition makeIdentifierGeneratorDefinition(
|
||||
String name,
|
||||
XProperty idXProperty,
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators,
|
||||
MetadataBuildingContext buildingContext) {
|
||||
if ( localGenerators != null ) {
|
||||
final IdentifierGeneratorDefinition result = localGenerators.get( name );
|
||||
if ( result != null ) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
final IdentifierGeneratorDefinition globalDefinition =
|
||||
buildingContext.getMetadataCollector().getIdentifierGenerator( name );
|
||||
if ( globalDefinition != null ) {
|
||||
return globalDefinition;
|
||||
}
|
||||
|
||||
LOG.debugf( "Could not resolve explicit IdentifierGeneratorDefinition - using implicit interpretation (%s)", name );
|
||||
|
||||
final GeneratedValue generatedValue = idXProperty.getAnnotation( GeneratedValue.class );
|
||||
if ( generatedValue == null ) {
|
||||
// this should really never happen, but it's easy to protect against it...
|
||||
return new IdentifierGeneratorDefinition( DEFAULT_ID_GEN_STRATEGY, DEFAULT_ID_GEN_STRATEGY );
|
||||
}
|
||||
|
||||
return IdentifierGeneratorDefinition.createImplicit(
|
||||
name,
|
||||
buildingContext
|
||||
.getBootstrapContext()
|
||||
.getReflectionManager()
|
||||
.toClass( idXProperty.getType() ),
|
||||
generatedValue.generator(),
|
||||
buildingContext.getBuildingOptions().getIdGenerationTypeInterpreter(),
|
||||
interpretGenerationType( generatedValue )
|
||||
);
|
||||
}
|
||||
|
||||
private static GenerationType interpretGenerationType(GeneratedValue generatedValueAnn) {
|
||||
return generatedValueAnn.strategy() == null ? GenerationType.AUTO : generatedValueAnn.strategy();
|
||||
}
|
||||
|
||||
public static Map<String, IdentifierGeneratorDefinition> buildGenerators(
|
||||
XAnnotatedElement annotatedElement,
|
||||
MetadataBuildingContext context) {
|
||||
|
||||
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
|
||||
final Map<String, IdentifierGeneratorDefinition> generators = new HashMap<>();
|
||||
|
||||
final TableGenerators tableGenerators = annotatedElement.getAnnotation( TableGenerators.class );
|
||||
if ( tableGenerators != null ) {
|
||||
for ( TableGenerator tableGenerator : tableGenerators.value() ) {
|
||||
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( tableGenerator, context );
|
||||
generators.put(
|
||||
idGenerator.getName(),
|
||||
idGenerator
|
||||
);
|
||||
metadataCollector.addIdentifierGenerator( idGenerator );
|
||||
}
|
||||
}
|
||||
|
||||
final SequenceGenerators sequenceGenerators = annotatedElement.getAnnotation( SequenceGenerators.class );
|
||||
if ( sequenceGenerators != null ) {
|
||||
for ( SequenceGenerator sequenceGenerator : sequenceGenerators.value() ) {
|
||||
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( sequenceGenerator, context );
|
||||
generators.put(
|
||||
idGenerator.getName(),
|
||||
idGenerator
|
||||
);
|
||||
metadataCollector.addIdentifierGenerator( idGenerator );
|
||||
}
|
||||
}
|
||||
|
||||
final TableGenerator tableGenerator = annotatedElement.getAnnotation( TableGenerator.class );
|
||||
if ( tableGenerator != null ) {
|
||||
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( tableGenerator, context );
|
||||
generators.put( idGenerator.getName(), idGenerator );
|
||||
metadataCollector.addIdentifierGenerator( idGenerator );
|
||||
|
||||
}
|
||||
|
||||
final SequenceGenerator sequenceGenerator = annotatedElement.getAnnotation( SequenceGenerator.class );
|
||||
if ( sequenceGenerator != null ) {
|
||||
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( sequenceGenerator, context );
|
||||
generators.put( idGenerator.getName(), idGenerator );
|
||||
metadataCollector.addIdentifierGenerator( idGenerator );
|
||||
}
|
||||
|
||||
final GenericGenerator genericGenerator = annotatedElement.getAnnotation( GenericGenerator.class );
|
||||
if ( genericGenerator != null ) {
|
||||
final IdentifierGeneratorDefinition idGenerator = buildIdGenerator( genericGenerator, context );
|
||||
generators.put( idGenerator.getName(), idGenerator );
|
||||
metadataCollector.addIdentifierGenerator( idGenerator );
|
||||
}
|
||||
|
||||
return generators;
|
||||
}
|
||||
|
||||
static String generatorType(
|
||||
MetadataBuildingContext context,
|
||||
XClass entityXClass,
|
||||
boolean isComponent,
|
||||
GeneratedValue generatedValue) {
|
||||
if ( isComponent ) {
|
||||
//a component must not have any generator
|
||||
return DEFAULT_ID_GEN_STRATEGY;
|
||||
}
|
||||
else {
|
||||
return generatedValue == null ? DEFAULT_ID_GEN_STRATEGY : generatorType( generatedValue, entityXClass, context );
|
||||
}
|
||||
}
|
||||
|
||||
static String generatorType(GeneratedValue generatedValue, final XClass javaClass, MetadataBuildingContext context) {
|
||||
return context.getBuildingOptions().getIdGenerationTypeInterpreter()
|
||||
.determineGeneratorName(
|
||||
generatedValue.strategy(),
|
||||
new IdGeneratorStrategyInterpreter.GeneratorNameDeterminationContext() {
|
||||
Class<?> javaType = null;
|
||||
@Override
|
||||
public Class<?> getIdType() {
|
||||
if ( javaType == null ) {
|
||||
javaType = context.getBootstrapContext().getReflectionManager().toClass( javaClass );
|
||||
}
|
||||
return javaType;
|
||||
}
|
||||
@Override
|
||||
public String getGeneratedValueGeneratorName() {
|
||||
return generatedValue.generator();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static IdentifierGeneratorDefinition buildIdGenerator(
|
||||
Annotation generatorAnnotation,
|
||||
MetadataBuildingContext context) {
|
||||
if ( generatorAnnotation == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final IdentifierGeneratorDefinition.Builder definitionBuilder = new IdentifierGeneratorDefinition.Builder();
|
||||
if ( generatorAnnotation instanceof TableGenerator ) {
|
||||
context.getBuildingOptions().getIdGenerationTypeInterpreter().interpretTableGenerator(
|
||||
(TableGenerator) generatorAnnotation,
|
||||
definitionBuilder
|
||||
);
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev( "Add table generator with name: {0}", definitionBuilder.getName() );
|
||||
}
|
||||
}
|
||||
else if ( generatorAnnotation instanceof SequenceGenerator ) {
|
||||
context.getBuildingOptions().getIdGenerationTypeInterpreter().interpretSequenceGenerator(
|
||||
(SequenceGenerator) generatorAnnotation,
|
||||
definitionBuilder
|
||||
);
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev( "Add sequence generator with name: {0}", definitionBuilder.getName() );
|
||||
}
|
||||
}
|
||||
else if ( generatorAnnotation instanceof GenericGenerator ) {
|
||||
final GenericGenerator genericGenerator = (GenericGenerator) generatorAnnotation;
|
||||
definitionBuilder.setName( genericGenerator.name() );
|
||||
final String strategy = genericGenerator.type().equals(Generator.class)
|
||||
? genericGenerator.strategy()
|
||||
: genericGenerator.type().getName();
|
||||
definitionBuilder.setStrategy( strategy );
|
||||
for ( Parameter parameter : genericGenerator.parameters() ) {
|
||||
definitionBuilder.addParam( parameter.name(), parameter.value() );
|
||||
}
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev( "Add generic generator with name: {0}", definitionBuilder.getName() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure( "Unknown Generator annotation: " + generatorAnnotation );
|
||||
}
|
||||
return definitionBuilder.build();
|
||||
}
|
||||
|
||||
private static void checkGeneratorClass(Class<? extends Generator> generatorClass) {
|
||||
if ( !BeforeExecutionGenerator.class.isAssignableFrom( generatorClass )
|
||||
&& !OnExecutionGenerator.class.isAssignableFrom( generatorClass ) ) {
|
||||
throw new MappingException("Generator class '" + generatorClass.getName()
|
||||
+ "' must implement either 'BeforeExecutionGenerator' or 'OnExecutionGenerator'");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkGeneratorInterfaces(Class<? extends Generator> generatorClass) {
|
||||
// we don't yet support the additional "fancy" operations of
|
||||
// IdentifierGenerator with regular generators, though this
|
||||
// would be extremely easy to add if anyone asks for it
|
||||
if ( IdentifierGenerator.class.isAssignableFrom( generatorClass ) ) {
|
||||
throw new AnnotationException("Generator class '" + generatorClass.getName()
|
||||
+ "' implements 'IdentifierGenerator' and may not be used with '@ValueGenerationType'");
|
||||
}
|
||||
if ( ExportableProducer.class.isAssignableFrom( generatorClass ) ) {
|
||||
throw new AnnotationException("Generator class '" + generatorClass.getName()
|
||||
+ "' implements 'ExportableProducer' and may not be used with '@ValueGenerationType'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In case the given annotation is a value generator annotation, the corresponding value generation strategy to be
|
||||
* applied to the given property is returned, {@code null} otherwise.
|
||||
* Instantiates the given generator annotation type, initializing it with the given instance of the corresponding
|
||||
* generator annotation and the property's type.
|
||||
*/
|
||||
static GeneratorCreator generatorCreator(XProperty property, Annotation annotation) {
|
||||
final Member member = HCANNHelper.getUnderlyingMember( property );
|
||||
final Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
final ValueGenerationType generatorAnnotation = annotationType.getAnnotation( ValueGenerationType.class );
|
||||
if ( generatorAnnotation == null ) {
|
||||
return null;
|
||||
}
|
||||
final Class<? extends Generator> generatorClass = generatorAnnotation.generatedBy();
|
||||
checkGeneratorClass( generatorClass );
|
||||
checkGeneratorInterfaces( generatorClass );
|
||||
return creationContext -> {
|
||||
final Generator generator =
|
||||
instantiateGenerator(
|
||||
annotation,
|
||||
member,
|
||||
annotationType,
|
||||
creationContext,
|
||||
GeneratorCreationContext.class,
|
||||
generatorClass
|
||||
);
|
||||
callInitialize( annotation, member, creationContext, generator );
|
||||
checkVersionGenerationAlways( property, generator );
|
||||
return generator;
|
||||
};
|
||||
}
|
||||
|
||||
static IdentifierGeneratorCreator identifierGeneratorCreator(XProperty idProperty, Annotation annotation) {
|
||||
final Member member = HCANNHelper.getUnderlyingMember( idProperty );
|
||||
final Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
final IdGeneratorType idGeneratorType = annotationType.getAnnotation( IdGeneratorType.class );
|
||||
assert idGeneratorType != null;
|
||||
return creationContext -> {
|
||||
final Class<? extends Generator> generatorClass = idGeneratorType.value();
|
||||
checkGeneratorClass( generatorClass );
|
||||
final Generator generator =
|
||||
instantiateGenerator(
|
||||
annotation,
|
||||
member,
|
||||
annotationType,
|
||||
creationContext,
|
||||
CustomIdGeneratorCreationContext.class,
|
||||
generatorClass
|
||||
);
|
||||
callInitialize( annotation, member, creationContext, generator );
|
||||
checkIdGeneratorTiming( annotationType, generator );
|
||||
return generator;
|
||||
};
|
||||
}
|
||||
|
||||
private static <C, G extends Generator> G instantiateGenerator(
|
||||
Annotation annotation,
|
||||
Member member,
|
||||
Class<? extends Annotation> annotationType,
|
||||
C creationContext,
|
||||
Class<C> contextClass,
|
||||
Class<? extends G> generatorClass) {
|
||||
try {
|
||||
try {
|
||||
return generatorClass
|
||||
.getConstructor( annotationType, Member.class, contextClass )
|
||||
.newInstance( annotation, member, creationContext);
|
||||
}
|
||||
catch (NoSuchMethodException ignore) {
|
||||
try {
|
||||
return generatorClass
|
||||
.getConstructor( annotationType )
|
||||
.newInstance( annotation );
|
||||
}
|
||||
catch (NoSuchMethodException i) {
|
||||
return generatorClass.newInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
|
||||
throw new HibernateException(
|
||||
"Could not instantiate generator of type '" + generatorClass.getName() + "'",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static <A extends Annotation> void callInitialize(
|
||||
A annotation,
|
||||
Member member,
|
||||
GeneratorCreationContext creationContext,
|
||||
Generator generator) {
|
||||
if ( generator instanceof AnnotationBasedGenerator) {
|
||||
// This will cause a CCE in case the generation type doesn't match the annotation type; As this would be
|
||||
// a programming error of the generation type developer and thus should show up during testing, we don't
|
||||
// check this explicitly; If required, this could be done e.g. using ClassMate
|
||||
@SuppressWarnings("unchecked")
|
||||
final AnnotationBasedGenerator<A> generation = (AnnotationBasedGenerator<A>) generator;
|
||||
generation.initialize( annotation, member, creationContext );
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkVersionGenerationAlways(XProperty property, Generator generator) {
|
||||
if ( property.isAnnotationPresent(Version.class) ) {
|
||||
if ( !generator.generatesOnInsert() ) {
|
||||
throw new AnnotationException("Property '" + property.getName()
|
||||
+ "' is annotated '@Version' but has a 'Generator' which does not generate on inserts"
|
||||
);
|
||||
}
|
||||
if ( !generator.generatesOnUpdate() ) {
|
||||
throw new AnnotationException("Property '" + property.getName()
|
||||
+ "' is annotated '@Version' but has a 'Generator' which does not generate on updates"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkIdGeneratorTiming(Class<? extends Annotation> annotationType, Generator generator) {
|
||||
if ( !generator.generatesOnInsert() ) {
|
||||
throw new MappingException( "Annotation '" + annotationType
|
||||
+ "' is annotated 'IdGeneratorType' but the given 'Generator' does not generate on inserts");
|
||||
}
|
||||
if ( generator.generatesOnUpdate() ) {
|
||||
throw new MappingException( "Annotation '" + annotationType
|
||||
+ "' is annotated 'IdGeneratorType' but the given 'Generator' generates on updates (it must generate only on inserts)");
|
||||
}
|
||||
}
|
||||
|
||||
static void createIdGenerator(
|
||||
SimpleValue idValue,
|
||||
Map<String, IdentifierGeneratorDefinition> classGenerators,
|
||||
MetadataBuildingContext context,
|
||||
XClass entityClass,
|
||||
XProperty idProperty) {
|
||||
//manage composite related metadata
|
||||
//guess if its a component and find id data access (property, field etc)
|
||||
final GeneratedValue generatedValue = idProperty.getAnnotation( GeneratedValue.class );
|
||||
final String generatorType = generatorType( context, entityClass, isCompositeId( entityClass, idProperty ), generatedValue );
|
||||
final String generatorName = generatedValue == null ? "" : generatedValue.generator();
|
||||
if ( isGlobalGeneratorNameGlobal( context ) ) {
|
||||
buildGenerators( idProperty, context );
|
||||
context.getMetadataCollector().addSecondPass( new IdGeneratorResolverSecondPass(
|
||||
idValue,
|
||||
idProperty,
|
||||
generatorType,
|
||||
generatorName,
|
||||
context
|
||||
) );
|
||||
}
|
||||
else {
|
||||
//clone classGenerator and override with local values
|
||||
final Map<String, IdentifierGeneratorDefinition> generators = new HashMap<>( classGenerators );
|
||||
generators.putAll( buildGenerators( idProperty, context ) );
|
||||
makeIdGenerator( idValue, idProperty, generatorType, generatorName, context, generators );
|
||||
}
|
||||
}
|
||||
|
||||
static IdentifierGeneratorDefinition createForeignGenerator(PropertyData mapsIdProperty) {
|
||||
final IdentifierGeneratorDefinition.Builder foreignGeneratorBuilder =
|
||||
new IdentifierGeneratorDefinition.Builder();
|
||||
foreignGeneratorBuilder.setName( "Hibernate-local--foreign generator" );
|
||||
foreignGeneratorBuilder.setStrategy( "foreign" );
|
||||
foreignGeneratorBuilder.addParam( "property", mapsIdProperty.getPropertyName() );
|
||||
return foreignGeneratorBuilder.build();
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ import org.hibernate.usertype.UserCollectionType;
|
|||
|
||||
import jakarta.persistence.Column;
|
||||
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.makeIdGenerator;
|
||||
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
|
||||
|
||||
/**
|
||||
* A {@link CollectionBinder} for {@link org.hibernate.collection.spi.PersistentIdentifierBag id bags}
|
||||
|
|
|
@ -16,7 +16,7 @@ import org.hibernate.boot.spi.SecondPass;
|
|||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.makeIdGenerator;
|
||||
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
|
|
|
@ -28,6 +28,10 @@ import jakarta.persistence.Inheritance;
|
|||
import jakarta.persistence.InheritanceType;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
||||
import static jakarta.persistence.InheritanceType.SINGLE_TABLE;
|
||||
import static jakarta.persistence.InheritanceType.TABLE_PER_CLASS;
|
||||
import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass;
|
||||
|
||||
/**
|
||||
* Some extra data to the inheritance position of a class.
|
||||
*
|
||||
|
@ -73,16 +77,16 @@ public class InheritanceState {
|
|||
setType( inhAnn == null ? null : inhAnn.strategy() );
|
||||
}
|
||||
else {
|
||||
setType( inhAnn == null ? InheritanceType.SINGLE_TABLE : inhAnn.strategy() );
|
||||
setType( inhAnn == null ? SINGLE_TABLE : inhAnn.strategy() );
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasTable() {
|
||||
return !hasParents() || !InheritanceType.SINGLE_TABLE.equals( getType() );
|
||||
return !hasParents() || !SINGLE_TABLE.equals( getType() );
|
||||
}
|
||||
|
||||
public boolean hasDenormalizedTable() {
|
||||
return hasParents() && InheritanceType.TABLE_PER_CLASS.equals( getType() );
|
||||
return hasParents() && TABLE_PER_CLASS.equals( getType() );
|
||||
}
|
||||
|
||||
public static InheritanceState getInheritanceStateOfSuperEntity(XClass clazz, Map<XClass, InheritanceState> states) {
|
||||
|
@ -167,7 +171,7 @@ public class InheritanceState {
|
|||
return clazz;
|
||||
}
|
||||
else {
|
||||
InheritanceState state = InheritanceState.getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
|
||||
final InheritanceState state = getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
|
||||
if ( state != null ) {
|
||||
return state.getClassWithIdClass( true );
|
||||
}
|
||||
|
@ -210,7 +214,7 @@ public class InheritanceState {
|
|||
|
||||
accessType = determineDefaultAccessType();
|
||||
|
||||
ArrayList<PropertyData> elements = new ArrayList<>();
|
||||
final ArrayList<PropertyData> elements = new ArrayList<>();
|
||||
int idPropertyCount = 0;
|
||||
|
||||
for ( XClass classToProcessForMappedSuperclass : classesToProcessForMappedSuperclass ) {
|
||||
|
@ -219,7 +223,7 @@ public class InheritanceState {
|
|||
clazz,
|
||||
accessType
|
||||
);
|
||||
int currentIdPropertyCount = AnnotationBinder.addElementsOfClass(
|
||||
int currentIdPropertyCount = addElementsOfClass(
|
||||
elements,
|
||||
propertyContainer,
|
||||
buildingContext
|
||||
|
@ -248,7 +252,9 @@ public class InheritanceState {
|
|||
// Guess from identifier.
|
||||
// FIX: Shouldn't this be determined by the first attribute (i.e., field or property) with annotations, but without an
|
||||
// explicit Access annotation, according to JPA 2.0 spec 2.3.1: Default Access Type?
|
||||
for (XClass xclass = clazz; xclass != null && !Object.class.getName().equals(xclass.getName()); xclass = xclass.getSuperclass()) {
|
||||
for ( XClass xclass = clazz;
|
||||
xclass != null && !Object.class.getName().equals( xclass.getName() );
|
||||
xclass = xclass.getSuperclass() ) {
|
||||
if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
|
||||
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
|
||||
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
|
||||
|
|
|
@ -53,6 +53,7 @@ import static org.hibernate.boot.model.internal.AnnotatedClassType.EMBEDDABLE;
|
|||
import static org.hibernate.boot.model.internal.AnnotatedClassType.NONE;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.findPropertyByName;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isPrimitive;
|
||||
import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable;
|
||||
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
|
||||
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||
|
@ -365,7 +366,7 @@ public class MapBinder extends CollectionBinder {
|
|||
CollectionPropertyHolder holder,
|
||||
AccessType accessType,
|
||||
Class<? extends CompositeUserType<?>> compositeUserType) {
|
||||
getMap().setIndex( AnnotationBinder.fillComponent(
|
||||
getMap().setIndex( fillEmbeddable(
|
||||
holder,
|
||||
propertyPreloadedData( keyClass ),
|
||||
accessType,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -50,7 +50,10 @@ import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
|
|||
import static org.hibernate.internal.util.collections.CollectionHelper.setOf;
|
||||
|
||||
/**
|
||||
* Query binder
|
||||
* Responsible for reading named queries defined in annotations and registering
|
||||
* {@link org.hibernate.boot.query.NamedQueryDefinition} objects.
|
||||
*
|
||||
* @implNote This class is stateless, unlike most of the other "binders".
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
|
|
|
@ -51,7 +51,7 @@ import static org.hibernate.internal.util.StringHelper.unquote;
|
|||
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||
|
||||
/**
|
||||
* Table related operations
|
||||
* Stateful binder responsible for producing instances of {@link Table}.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 org.hibernate.annotations.TimeZoneStorage;
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.usertype.CompositeUserType;
|
||||
import org.hibernate.usertype.internal.OffsetDateTimeCompositeUserType;
|
||||
import org.hibernate.usertype.internal.ZonedDateTimeCompositeUserType;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import static org.hibernate.TimeZoneStorageStrategy.COLUMN;
|
||||
import static org.hibernate.dialect.TimeZoneSupport.NATIVE;
|
||||
|
||||
public class TimeZoneStorageHelper {
|
||||
|
||||
private static final String OFFSET_DATETIME_CLASS = OffsetDateTime.class.getName();
|
||||
private static final String ZONED_DATETIME_CLASS = ZonedDateTime.class.getName();
|
||||
|
||||
static Class<? extends CompositeUserType<?>> resolveTimeZoneStorageCompositeUserType(
|
||||
XProperty property,
|
||||
XClass returnedClass,
|
||||
MetadataBuildingContext context) {
|
||||
if ( useColumnForTimeZoneStorage( property, context ) ) {
|
||||
String returnedClassName = returnedClass.getName();
|
||||
if ( OFFSET_DATETIME_CLASS.equals( returnedClassName ) ) {
|
||||
return OffsetDateTimeCompositeUserType.class;
|
||||
}
|
||||
else if ( ZONED_DATETIME_CLASS.equals( returnedClassName ) ) {
|
||||
return ZonedDateTimeCompositeUserType.class;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isZonedDateTimeClass(String returnedClassName) {
|
||||
return OFFSET_DATETIME_CLASS.equals( returnedClassName )
|
||||
|| ZONED_DATETIME_CLASS.equals( returnedClassName );
|
||||
}
|
||||
|
||||
static boolean useColumnForTimeZoneStorage(XAnnotatedElement element, MetadataBuildingContext context) {
|
||||
final TimeZoneStorage timeZoneStorage = element.getAnnotation( TimeZoneStorage.class );
|
||||
if ( timeZoneStorage == null ) {
|
||||
if ( element instanceof XProperty ) {
|
||||
XProperty property = (XProperty) element;
|
||||
return isZonedDateTimeClass( property.getType().getName() )
|
||||
//no @TimeZoneStorage annotation, so we need to use the default storage strategy
|
||||
&& context.getBuildingOptions().getDefaultTimeZoneStorage() == COLUMN;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch ( timeZoneStorage.value() ) {
|
||||
case COLUMN:
|
||||
return true;
|
||||
case AUTO:
|
||||
// if the db has native support for timezones, we use that, not a column
|
||||
return context.getBuildingOptions().getTimeZoneSupport() != NATIVE;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,16 +46,26 @@ import jakarta.persistence.OneToOne;
|
|||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumns;
|
||||
|
||||
import static org.hibernate.boot.model.internal.AnnotationBinder.matchIgnoreNotFoundWithFetchType;
|
||||
import static jakarta.persistence.ConstraintMode.NO_CONSTRAINT;
|
||||
import static jakarta.persistence.ConstraintMode.PROVIDER_DEFAULT;
|
||||
import static jakarta.persistence.FetchType.EAGER;
|
||||
import static jakarta.persistence.FetchType.LAZY;
|
||||
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.getPath;
|
||||
import static org.hibernate.boot.model.internal.BinderHelper.isDefault;
|
||||
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
|
||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||
|
||||
/**
|
||||
* Responsible for interpreting {@link ManyToOne} and {@link OneToOne} associations
|
||||
* and producing mapping model objects of type {@link org.hibernate.mapping.ManyToOne}
|
||||
* and {@link org.hibernate.mapping.OneToOne}.
|
||||
*
|
||||
* @implNote This class is stateless, unlike most of the other "binders".
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class ToOneBinder {
|
||||
|
@ -243,7 +253,8 @@ public class ToOneBinder {
|
|||
PropertyData inferredData,
|
||||
boolean isIdentifierMapper,
|
||||
PropertyBinder propertyBinder,
|
||||
org.hibernate.mapping.ManyToOne value, XProperty property,
|
||||
org.hibernate.mapping.ManyToOne value,
|
||||
XProperty property,
|
||||
boolean hasSpecjManyToOne,
|
||||
String propertyName) {
|
||||
|
||||
|
@ -265,7 +276,7 @@ public class ToOneBinder {
|
|||
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
|
||||
propertyBinder.setCascade( cascadeStrategy );
|
||||
propertyBinder.setProperty( property );
|
||||
propertyBinder.setXToMany( true );
|
||||
propertyBinder.setToMany( true );
|
||||
|
||||
final JoinColumn joinColumn = property.getAnnotation( JoinColumn.class );
|
||||
final JoinColumns joinColumns = property.getAnnotation( JoinColumns.class );
|
||||
|
@ -346,7 +357,7 @@ public class ToOneBinder {
|
|||
// LazyToOne takes precedent
|
||||
final LazyToOne lazy = property.getAnnotation( LazyToOne.class );
|
||||
boolean eager = lazy.value() == LazyToOneOption.FALSE;
|
||||
if ( eager && fetchType == FetchType.LAZY ) {
|
||||
if ( eager && fetchType == LAZY ) {
|
||||
// conflicts with non-default setting
|
||||
throw new AnnotationException("Association '" + getPath(propertyHolder, inferredData)
|
||||
+ "' is marked 'fetch=LAZY' and '@LazyToOne(FALSE)'");
|
||||
|
@ -354,7 +365,7 @@ public class ToOneBinder {
|
|||
return eager;
|
||||
}
|
||||
else {
|
||||
return fetchType == FetchType.EAGER;
|
||||
return fetchType == EAGER;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,20 +590,20 @@ public class ToOneBinder {
|
|||
}
|
||||
else {
|
||||
ConstraintMode mode = joinColumns.value();
|
||||
return mode == ConstraintMode.NO_CONSTRAINT
|
||||
|| mode == ConstraintMode.PROVIDER_DEFAULT && context.getBuildingOptions().isNoConstraintByDefault();
|
||||
return mode == NO_CONSTRAINT
|
||||
|| mode == PROVIDER_DEFAULT && context.getBuildingOptions().isNoConstraintByDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getReferenceEntityName(PropertyData propertyData, XClass targetEntity, MetadataBuildingContext context) {
|
||||
return AnnotationBinder.isDefault( targetEntity, context )
|
||||
return isDefault( targetEntity, context )
|
||||
? propertyData.getClassOrElementName()
|
||||
: targetEntity.getName();
|
||||
}
|
||||
|
||||
public static String getReferenceEntityName(PropertyData propertyData, MetadataBuildingContext context) {
|
||||
final XClass targetEntity = getTargetEntity( propertyData, context );
|
||||
return AnnotationBinder.isDefault( targetEntity, context )
|
||||
return isDefault( targetEntity, context )
|
||||
? propertyData.getClassOrElementName()
|
||||
: targetEntity.getName();
|
||||
}
|
||||
|
@ -613,4 +624,14 @@ public class ToOneBinder {
|
|||
}
|
||||
throw new AssertionFailure("Unexpected discovery of a targetEntity: " + property.getName() );
|
||||
}
|
||||
|
||||
private static void matchIgnoreNotFoundWithFetchType(
|
||||
String entity,
|
||||
String association,
|
||||
NotFoundAction notFoundAction,
|
||||
FetchType fetchType) {
|
||||
if ( notFoundAction != null && fetchType == LAZY ) {
|
||||
LOG.ignoreNotFoundWithFetchTypeLazy( entity, association );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1730,8 +1730,8 @@ public interface CoreMessageLogger extends BasicLogger {
|
|||
void usingJtaPlatform(String jtaPlatformClassName);
|
||||
|
||||
@LogMessage(level = WARN)
|
||||
@Message(value = "`.%1$s.%2$s` uses both @NotFound and FetchType.LAZY. @ManyToOne and " +
|
||||
"@OneToOne associations mapped with `@NotFound` are forced to EAGER fetching.", id = 491)
|
||||
@Message(value = "'%1$s.%2$s' uses both @NotFound and FetchType.LAZY. @ManyToOne and " +
|
||||
"@OneToOne associations mapped with @NotFound are forced to EAGER fetching.", id = 491)
|
||||
void ignoreNotFoundWithFetchTypeLazy(String entity, String association);
|
||||
|
||||
@LogMessage(level = INFO)
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.hibernate.annotations.JoinColumnOrFormula;
|
|||
import org.hibernate.annotations.JoinFormula;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.boot.model.internal.AnnotationBinder;
|
||||
import org.hibernate.boot.model.internal.ToOneBinder;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
|
@ -44,10 +44,10 @@ public class JoinFormulaManyToOneNotIgnoreLazyFetchingTest extends BaseEntityMan
|
|||
|
||||
@Rule
|
||||
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
|
||||
Logger.getMessageLogger( CoreMessageLogger.class, AnnotationBinder.class.getName() )
|
||||
Logger.getMessageLogger( CoreMessageLogger.class, ToOneBinder.class.getName() )
|
||||
);
|
||||
|
||||
private Triggerable triggerable = logInspection.watchForLogMessages( "HHH000491" );
|
||||
private final Triggerable triggerable = logInspection.watchForLogMessages( "HHH000491" );
|
||||
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.hibernate.annotations.JoinColumnOrFormula;
|
|||
import org.hibernate.annotations.JoinFormula;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.boot.model.internal.AnnotationBinder;
|
||||
import org.hibernate.boot.model.internal.ToOneBinder;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
|
@ -44,10 +44,10 @@ public class JoinFormulaOneToOneNotIgnoreLazyFetchingTest extends BaseEntityMana
|
|||
|
||||
@Rule
|
||||
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
|
||||
Logger.getMessageLogger( CoreMessageLogger.class, AnnotationBinder.class.getName() )
|
||||
Logger.getMessageLogger( CoreMessageLogger.class, ToOneBinder.class.getName() )
|
||||
);
|
||||
|
||||
private Triggerable triggerable = logInspection.watchForLogMessages( "HHH000491" );
|
||||
private final Triggerable triggerable = logInspection.watchForLogMessages( "HHH000491" );
|
||||
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,6 @@ import jakarta.persistence.metamodel.Type;
|
|||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.orm.test.legacy.I;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
|
@ -105,7 +104,6 @@ public class MetadataTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testBuildingMetamodelWithParameterizedCollection() {
|
||||
Metadata metadata = new MetadataSources()
|
||||
.addAnnotatedClass(WithGenericCollection.class)
|
||||
|
@ -441,7 +439,4 @@ public class MetadataTest {
|
|||
}
|
||||
}
|
||||
|
||||
//todo test plural
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue