package org.hibernate.cfg;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.MapKeyColumn;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.MapKeyJoinColumns;
import javax.persistence.MappedSuperclass;
import javax.persistence.MapsId;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.NamedStoredProcedureQueries;
import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderColumn;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.PrimaryKeyJoinColumns;
import javax.persistence.SequenceGenerator;
import javax.persistence.SequenceGenerators;
import javax.persistence.SharedCacheMode;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.TableGenerators;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.EntityMode;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.DiscriminatorOptions;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchProfile;
import org.hibernate.annotations.FetchProfiles;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterDefs;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.GenericGenerators;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.LazyGroup;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.ListIndexBase;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.OrderBy;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Parent;
import org.hibernate.annotations.Proxy;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.SortComparator;
import org.hibernate.annotations.SortNatural;
import org.hibernate.annotations.Source;
import org.hibernate.annotations.Tuplizer;
import org.hibernate.annotations.Tuplizers;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.hibernate.annotations.Where;
import org.hibernate.annotations.common.reflection.ClassLoadingException;
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.XPackage;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.IdGeneratorStrategyInterpreter;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.InFlightMetadataCollector.EntityTableXref;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.CollectionBinder;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.HCANNHelper;
import org.hibernate.cfg.annotations.MapKeyColumnDelegator;
import org.hibernate.cfg.annotations.MapKeyJoinColumnDelegator;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.cfg.annotations.PropertyBinder;
import org.hibernate.cfg.annotations.QueryBinder;
import org.hibernate.cfg.annotations.SimpleValueBinder;
import org.hibernate.cfg.annotations.TableBinder;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.PropertyPath;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Constraint;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UnionSubclass;
import static org.hibernate.internal.CoreLogging.messageLogger;
@SuppressWarnings("unchecked")
public final class AnnotationBinder {
private static final CoreMessageLogger LOG = messageLogger( AnnotationBinder.class );
private AnnotationBinder() {
}
public static void bindDefaults(MetadataBuildingContext context) {
Map defaults = context.getBootstrapContext().getReflectionManager().getDefaults();
{
List<SequenceGenerator> anns = ( List<SequenceGenerator> ) defaults.get( SequenceGenerator.class );
if ( anns != null ) {
for ( SequenceGenerator ann : anns ) {
IdentifierGeneratorDefinition idGen = buildIdGenerator( ann, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
}
}
{
List<TableGenerator> anns = ( List<TableGenerator> ) defaults.get( TableGenerator.class );
if ( anns != null ) {
for ( TableGenerator ann : anns ) {
IdentifierGeneratorDefinition idGen = buildIdGenerator( ann, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
}
}
{
List<TableGenerators> anns = (List<TableGenerators>) defaults.get( TableGenerators.class );
if ( anns != null ) {
anns.forEach( tableGenerators -> {
for ( TableGenerator tableGenerator : tableGenerators.value() ) {
IdentifierGeneratorDefinition idGen = buildIdGenerator( tableGenerator, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
} );
}
}
{
List<SequenceGenerators> anns = (List<SequenceGenerators>) defaults.get( SequenceGenerators.class );
if ( anns != null ) {
anns.forEach( sequenceGenerators -> {
for ( SequenceGenerator ann : sequenceGenerators.value() ) {
IdentifierGeneratorDefinition idGen = buildIdGenerator( ann, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
} );
}
}
{
List<NamedQuery> anns = ( List<NamedQuery> ) defaults.get( NamedQuery.class );
if ( anns != null ) {
for ( NamedQuery ann : anns ) {
QueryBinder.bindQuery( ann, context, true );
}
}
}
{
List<NamedNativeQuery> anns = ( List<NamedNativeQuery> ) defaults.get( NamedNativeQuery.class );
if ( anns != null ) {
for ( NamedNativeQuery ann : anns ) {
QueryBinder.bindNativeQuery( ann, context, true );
}
}
}
{
List<SqlResultSetMapping> anns = ( List<SqlResultSetMapping> ) defaults.get( SqlResultSetMapping.class );
if ( anns != null ) {
for ( SqlResultSetMapping ann : anns ) {
QueryBinder.bindSqlResultSetMapping( ann, context, true );
}
}
}
{
final List<NamedStoredProcedureQuery> annotations =
(List<NamedStoredProcedureQuery>) defaults.get( NamedStoredProcedureQuery.class );
if ( annotations != null ) {
for ( NamedStoredProcedureQuery annotation : annotations ) {
bindNamedStoredProcedureQuery( annotation, context, true );
}
}
}
{
final List<NamedStoredProcedureQueries> annotations =
(List<NamedStoredProcedureQueries>) defaults.get( NamedStoredProcedureQueries.class );
if ( annotations != null ) {
for ( NamedStoredProcedureQueries annotation : annotations ) {
bindNamedStoredProcedureQueries( annotation, context, true );
}
}
}
}
public static void bindPackage(String packageName, MetadataBuildingContext context) {
XPackage pckg;
try {
pckg = context.getBootstrapContext().getReflectionManager().packageForName( packageName );
}
catch (ClassLoadingException e) {
LOG.packageNotFound( packageName );
return;
}
catch ( ClassNotFoundException cnf ) {
LOG.packageNotFound( packageName );
return;
}
if ( pckg.isAnnotationPresent( SequenceGenerator.class ) ) {
SequenceGenerator ann = pckg.getAnnotation( SequenceGenerator.class );
IdentifierGeneratorDefinition idGen = buildIdGenerator( ann, context );
context.getMetadataCollector().addIdentifierGenerator( idGen );
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add sequence generator with name: {0}", idGen.getName() );
}
}
if ( pckg.isAnnotationPresent( SequenceGenerators.class ) ) {
SequenceGenerators ann = pckg.getAnnotation( SequenceGenerators.class );
for ( SequenceGenerator tableGenerator : ann.value() ) {
context.getMetadataCollector().addIdentifierGenerator( buildIdGenerator( tableGenerator, context ) );
}
}
if ( pckg.isAnnotationPresent( TableGenerator.class ) ) {
TableGenerator ann = pckg.getAnnotation( TableGenerator.class );
IdentifierGeneratorDefinition idGen = buildIdGenerator( ann, context );
context.getMetadataCollector().addIdentifierGenerator( idGen );
}
if ( pckg.isAnnotationPresent( TableGenerators.class ) ) {
TableGenerators ann = pckg.getAnnotation( TableGenerators.class );
for ( TableGenerator tableGenerator : ann.value() ) {
context.getMetadataCollector().addIdentifierGenerator( buildIdGenerator( tableGenerator, context ) );
}
}
bindGenericGenerators( pckg, context );
bindQueries( pckg, context );
bindFilterDefs( pckg, context );
bindTypeDefs( pckg, context );
bindFetchProfiles( pckg, context );
BinderHelper.bindAnyMetaDefs( pckg, context );
}
private static void bindGenericGenerators(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
GenericGenerator defAnn = annotatedElement.getAnnotation( GenericGenerator.class );
GenericGenerators defsAnn = annotatedElement.getAnnotation( GenericGenerators.class );
if ( defAnn != null ) {
bindGenericGenerator( defAnn, context );
}
if ( defsAnn != null ) {
for ( GenericGenerator def : defsAnn.value() ) {
bindGenericGenerator( def, context );
}
}
}
private static void bindGenericGenerator(GenericGenerator def, MetadataBuildingContext context) {
context.getMetadataCollector().addIdentifierGenerator( buildIdGenerator( def, context ) );
}
private static void bindQueries(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
{
SqlResultSetMapping ann = annotatedElement.getAnnotation( SqlResultSetMapping.class );
QueryBinder.bindSqlResultSetMapping( ann, context, false );
}
{
SqlResultSetMappings ann = annotatedElement.getAnnotation( SqlResultSetMappings.class );
if ( ann != null ) {
for ( SqlResultSetMapping current : ann.value() ) {
QueryBinder.bindSqlResultSetMapping( current, context, false );
}
}
}
{
NamedQuery ann = annotatedElement.getAnnotation( NamedQuery.class );
QueryBinder.bindQuery( ann, context, false );
}
{
org.hibernate.annotations.NamedQuery ann = annotatedElement.getAnnotation(
org.hibernate.annotations.NamedQuery.class
);
QueryBinder.bindQuery( ann, context );
}
{
NamedQueries ann = annotatedElement.getAnnotation( NamedQueries.class );
QueryBinder.bindQueries( ann, context, false );
}
{
org.hibernate.annotations.NamedQueries ann = annotatedElement.getAnnotation(
org.hibernate.annotations.NamedQueries.class
);
QueryBinder.bindQueries( ann, context );
}
{
NamedNativeQuery ann = annotatedElement.getAnnotation( NamedNativeQuery.class );
QueryBinder.bindNativeQuery( ann, context, false );
}
{
org.hibernate.annotations.NamedNativeQuery ann = annotatedElement.getAnnotation(
org.hibernate.annotations.NamedNativeQuery.class
);
QueryBinder.bindNativeQuery( ann, context );
}
{
NamedNativeQueries ann = annotatedElement.getAnnotation( NamedNativeQueries.class );
QueryBinder.bindNativeQueries( ann, context, false );
}
{
org.hibernate.annotations.NamedNativeQueries ann = annotatedElement.getAnnotation(
org.hibernate.annotations.NamedNativeQueries.class
);
QueryBinder.bindNativeQueries( ann, context );
}
bindNamedStoredProcedureQuery(
annotatedElement.getAnnotation( NamedStoredProcedureQuery.class ),
context,
false
);
bindNamedStoredProcedureQueries(
annotatedElement.getAnnotation( NamedStoredProcedureQueries.class ),
context,
false
);
}
private static void bindNamedStoredProcedureQueries(NamedStoredProcedureQueries annotation, MetadataBuildingContext context, boolean isDefault) {
if ( annotation != null ) {
for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) {
bindNamedStoredProcedureQuery( queryAnnotation, context, isDefault );
}
}
}
private static void bindNamedStoredProcedureQuery(NamedStoredProcedureQuery annotation, MetadataBuildingContext context, boolean isDefault) {
if ( annotation != null ) {
QueryBinder.bindNamedStoredProcedureQuery( annotation, context, isDefault );
}
}
private static IdentifierGeneratorDefinition buildIdGenerator(
java.lang.annotation.Annotation generatorAnn,
MetadataBuildingContext context) {
if ( generatorAnn == null ) {
return null;
}
IdentifierGeneratorDefinition.Builder definitionBuilder = new IdentifierGeneratorDefinition.Builder();
if ( context.getMappingDefaults().getImplicitSchemaName() != null ) {
definitionBuilder.addParam(
PersistentIdentifierGenerator.SCHEMA,
context.getMappingDefaults().getImplicitSchemaName()
);
}
if ( context.getMappingDefaults().getImplicitCatalogName() != null ) {
definitionBuilder.addParam(
PersistentIdentifierGenerator.CATALOG,
context.getMappingDefaults().getImplicitCatalogName()
);
}
if ( generatorAnn instanceof TableGenerator ) {
context.getBuildingOptions().getIdGenerationTypeInterpreter().interpretTableGenerator(
(TableGenerator) generatorAnn,
definitionBuilder
);
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add table generator with name: {0}", definitionBuilder.getName() );
}
}
else if ( generatorAnn instanceof SequenceGenerator ) {
context.getBuildingOptions().getIdGenerationTypeInterpreter().interpretSequenceGenerator(
(SequenceGenerator) generatorAnn,
definitionBuilder
);
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add sequence generator with name: {0}", definitionBuilder.getName() );
}
}
else if ( generatorAnn instanceof GenericGenerator ) {
GenericGenerator genGen = ( GenericGenerator ) generatorAnn;
definitionBuilder.setName( genGen.name() );
definitionBuilder.setStrategy( genGen.strategy() );
Parameter[] params = genGen.parameters();
for ( Parameter parameter : params ) {
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: " + generatorAnn );
}
return definitionBuilder.build();
}
public static void bindClass(
XClass clazzToProcess,
Map<XClass, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext context) throws MappingException {
if ( clazzToProcess.isAnnotationPresent( Entity.class )
&& clazzToProcess.isAnnotationPresent( MappedSuperclass.class ) ) {
throw new AnnotationException( "An entity cannot be annotated with both @Entity and @MappedSuperclass: "
+ clazzToProcess.getName() );
}
InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
AnnotatedClassType classType = context.getMetadataCollector().getClassType( clazzToProcess );
if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) ) {
bindQueries( clazzToProcess, context );
bindTypeDefs( clazzToProcess, context );
bindFilterDefs( clazzToProcess, context );
}
if ( !isEntityClassType( clazzToProcess, classType ) ) {
return;
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Binding entity from annotated class: %s", clazzToProcess.getName() );
}
PersistentClass superEntity = getSuperEntity(
clazzToProcess,
inheritanceStatePerClass,
context,
inheritanceState
);
PersistentClass persistentClass = makePersistentClass( inheritanceState, superEntity, context );
Entity entityAnn = clazzToProcess.getAnnotation( Entity.class );
org.hibernate.annotations.Entity hibEntityAnn = clazzToProcess.getAnnotation(
org.hibernate.annotations.Entity.class
);
EntityBinder entityBinder = new EntityBinder(
entityAnn,
hibEntityAnn,
clazzToProcess,
persistentClass,
context
);
entityBinder.setInheritanceState( inheritanceState );
bindQueries( clazzToProcess, context );
bindFilterDefs( clazzToProcess, context );
bindTypeDefs( clazzToProcess, context );
bindFetchProfiles( clazzToProcess, context );
BinderHelper.bindAnyMetaDefs( clazzToProcess, context );
String schema = "";
String table = "";
String catalog = "";
List<UniqueConstraintHolder> uniqueConstraints = new ArrayList<>();
javax.persistence.Table tabAnn = null;
if ( clazzToProcess.isAnnotationPresent( javax.persistence.Table.class ) ) {
tabAnn = clazzToProcess.getAnnotation( javax.persistence.Table.class );
table = tabAnn.name();
schema = tabAnn.schema();
catalog = tabAnn.catalog();
uniqueConstraints = TableBinder.buildUniqueConstraintHolders( tabAnn.uniqueConstraints() );
}
Ejb3JoinColumn[] inheritanceJoinedColumns = makeInheritanceJoinColumns(
clazzToProcess,
context,
inheritanceState,
superEntity
);
final Ejb3DiscriminatorColumn discriminatorColumn;
if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
discriminatorColumn = processSingleTableDiscriminatorProperties(
clazzToProcess,
context,
inheritanceState,
entityBinder
);
}
else if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
discriminatorColumn = processJoinedDiscriminatorProperties(
clazzToProcess,
context,
inheritanceState,
entityBinder
);
}
else {
discriminatorColumn = null;
}
entityBinder.setProxy( clazzToProcess.getAnnotation( Proxy.class ) );
entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) );
applyCacheSettings( entityBinder, clazzToProcess, context );
bindFilters( clazzToProcess, entityBinder, context );
entityBinder.bindEntity();
if ( inheritanceState.hasTable() ) {
Check checkAnn = clazzToProcess.getAnnotation( Check.class );
String constraints = checkAnn == null
? null
: checkAnn.constraints();
EntityTableXref denormalizedTableXref = inheritanceState.hasDenormalizedTable()
? context.getMetadataCollector().getEntityTableXref( superEntity.getEntityName() )
: null;
entityBinder.bindTable(
schema,
catalog,
table,
uniqueConstraints,
constraints,
denormalizedTableXref
);
}
else {
if ( clazzToProcess.isAnnotationPresent( Table.class ) ) {
LOG.invalidTableAnnotation( clazzToProcess.getName() );
}
if ( inheritanceState.getType() == InheritanceType.SINGLE_TABLE ) {
entityBinder.bindTableForDiscriminatedSubclass(
context.getMetadataCollector().getEntityTableXref( superEntity.getEntityName() )
);
}
}
PropertyHolder propertyHolder = PropertyHolderBuilder.buildPropertyHolder(
clazzToProcess,
persistentClass,
entityBinder,
context,
inheritanceStatePerClass
);
javax.persistence.SecondaryTable secTabAnn = clazzToProcess.getAnnotation(
javax.persistence.SecondaryTable.class
);
javax.persistence.SecondaryTables secTabsAnn = clazzToProcess.getAnnotation(
javax.persistence.SecondaryTables.class
);
entityBinder.firstLevelSecondaryTablesBinding( secTabAnn, secTabsAnn );
OnDelete onDeleteAnn = clazzToProcess.getAnnotation( OnDelete.class );
boolean onDeleteAppropriate = false;
final boolean isInheritanceRoot = !inheritanceState.hasParents();
final boolean hasSubclasses = inheritanceState.hasSiblings();
if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
if ( inheritanceState.hasParents() ) {
onDeleteAppropriate = true;
final JoinedSubclass jsc = ( JoinedSubclass ) persistentClass;
SimpleValue key = new DependantValue( context, jsc.getTable(), jsc.getIdentifier() );
jsc.setKey( key );
ForeignKey fk = clazzToProcess.getAnnotation( ForeignKey.class );
if ( fk != null && !BinderHelper.isEmptyAnnotationValue( fk.name() ) ) {
key.setForeignKeyName( fk.name() );
}
else {
final PrimaryKeyJoinColumn pkJoinColumn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class );
final PrimaryKeyJoinColumns pkJoinColumns = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class );
if ( pkJoinColumns != null && pkJoinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
key.setForeignKeyName( "none" );
}
else if ( pkJoinColumns != null && !StringHelper.isEmpty( pkJoinColumns.foreignKey().name() ) ) {
key.setForeignKeyName( pkJoinColumns.foreignKey().name() );
}
else if ( pkJoinColumn != null && pkJoinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) {
key.setForeignKeyName( "none" );
}
else if ( pkJoinColumn != null && !StringHelper.isEmpty( pkJoinColumn.foreignKey().name() ) ) {
key.setForeignKeyName( pkJoinColumn.foreignKey().name() );
}
}
if ( onDeleteAnn != null ) {
key.setCascadeDeleteEnabled( OnDeleteAction.CASCADE.equals( onDeleteAnn.action() ) );
}
else {
key.setCascadeDeleteEnabled( false );
}
context.getMetadataCollector().addSecondPass( new JoinedSubclassFkSecondPass( jsc, inheritanceJoinedColumns, key, context ) );
context.getMetadataCollector().addSecondPass( new CreateKeySecondPass( jsc ) );
}
if ( isInheritanceRoot ) {
if ( discriminatorColumn != null ) {
if ( hasSubclasses || !discriminatorColumn.isImplicit() ) {
bindDiscriminatorColumnToRootPersistentClass(
(RootClass) persistentClass,
discriminatorColumn,
entityBinder.getSecondaryTables(),
propertyHolder,
context
);
entityBinder.bindDiscriminatorValue();
}
}
}
}
else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
if ( isInheritanceRoot ) {
if ( hasSubclasses || !discriminatorColumn.isImplicit() ) {
bindDiscriminatorColumnToRootPersistentClass(
(RootClass) persistentClass,
discriminatorColumn,
entityBinder.getSecondaryTables(),
propertyHolder,
context
);
entityBinder.bindDiscriminatorValue();
}
}
}
if ( onDeleteAnn != null && !onDeleteAppropriate ) {
LOG.invalidOnDeleteAnnotation(propertyHolder.getEntityName());
}
HashMap<String, IdentifierGeneratorDefinition> classGenerators = buildGenerators( clazzToProcess, context );
final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess();
inheritanceState.postProcess( persistentClass, entityBinder );
final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
&& inheritanceState.hasParents();
Set<String> idPropertiesIfIdClass = new HashSet<>();
boolean isIdClass = mapAsIdClass(
inheritanceStatePerClass,
inheritanceState,
persistentClass,
entityBinder,
propertyHolder,
elementsToProcess,
idPropertiesIfIdClass,
context
);
if ( !isIdClass ) {
entityBinder.setWrapIdsInEmbeddedComponents( elementsToProcess.getIdPropertyCount() > 1 );
}
processIdPropertiesIfNotAlready(
inheritanceStatePerClass,
context,
persistentClass,
entityBinder,
propertyHolder,
classGenerators,
elementsToProcess,
subclassAndSingleTableStrategy,
idPropertiesIfIdClass
);
if ( !inheritanceState.hasParents() ) {
final RootClass rootClass = ( RootClass ) persistentClass;
context.getMetadataCollector().addSecondPass( new CreateKeySecondPass( rootClass ) );
}
else {
superEntity.addSubclass( ( Subclass ) persistentClass );
}
context.getMetadataCollector().addEntityBinding( persistentClass );
context.getMetadataCollector().addSecondPass(
new SecondaryTableSecondPass(
entityBinder,
propertyHolder,
clazzToProcess
)
);
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Table.class ) );
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Tables.class ) );
entityBinder.processComplementaryTableDefinitions( tabAnn );
}
private static Ejb3DiscriminatorColumn processSingleTableDiscriminatorProperties(
XClass clazzToProcess,
MetadataBuildingContext context,
InheritanceState inheritanceState,
EntityBinder entityBinder) {
final boolean isRoot = !inheritanceState.hasParents();
Ejb3DiscriminatorColumn discriminatorColumn = null;
javax.persistence.DiscriminatorColumn discAnn = clazzToProcess.getAnnotation(
javax.persistence.DiscriminatorColumn.class
);
DiscriminatorType discriminatorType = discAnn != null
? discAnn.discriminatorType()
: DiscriminatorType.STRING;
org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = clazzToProcess.getAnnotation(
org.hibernate.annotations.DiscriminatorFormula.class
);
if ( isRoot ) {
discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
discriminatorType,
discAnn,
discFormulaAnn,
context
);
}
if ( discAnn != null && !isRoot ) {
LOG.invalidDiscriminatorAnnotation( clazzToProcess.getName() );
}
final String discriminatorValue = clazzToProcess.isAnnotationPresent( DiscriminatorValue.class )
? clazzToProcess.getAnnotation( DiscriminatorValue.class ).value()
: null;
entityBinder.setDiscriminatorValue( discriminatorValue );
DiscriminatorOptions discriminatorOptions = clazzToProcess.getAnnotation( DiscriminatorOptions.class );
if ( discriminatorOptions != null) {
entityBinder.setForceDiscriminator( discriminatorOptions.force() );
entityBinder.setInsertableDiscriminator( discriminatorOptions.insert() );
}
return discriminatorColumn;
}
private static Ejb3DiscriminatorColumn processJoinedDiscriminatorProperties(
XClass clazzToProcess,
MetadataBuildingContext context,
InheritanceState inheritanceState,
EntityBinder entityBinder) {
if ( clazzToProcess.isAnnotationPresent( DiscriminatorFormula.class ) ) {
throw new MappingException( "@DiscriminatorFormula on joined inheritance not supported at this time" );
}
final DiscriminatorValue discriminatorValueAnnotation = clazzToProcess.getAnnotation( DiscriminatorValue.class );
final String discriminatorValue = discriminatorValueAnnotation != null
? clazzToProcess.getAnnotation( DiscriminatorValue.class ).value()
: null;
entityBinder.setDiscriminatorValue( discriminatorValue );
final DiscriminatorColumn discriminatorColumnAnnotation = clazzToProcess.getAnnotation( DiscriminatorColumn.class );
if ( !inheritanceState.hasParents() ) {
final boolean generateDiscriminatorColumn;
if ( discriminatorColumnAnnotation != null ) {
if ( context.getBuildingOptions().ignoreExplicitDiscriminatorsForJoinedInheritance() ) {
LOG.debugf( "Ignoring explicit DiscriminatorColumn annotation on ", clazzToProcess.getName() );
generateDiscriminatorColumn = false;
}
else {
LOG.applyingExplicitDiscriminatorColumnForJoined(
clazzToProcess.getName(),
AvailableSettings.IGNORE_EXPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS
);
generateDiscriminatorColumn = true;
}
}
else {
if ( context.getBuildingOptions().createImplicitDiscriminatorsForJoinedInheritance() ) {
LOG.debug( "Applying implicit DiscriminatorColumn using DiscriminatorColumn defaults" );
generateDiscriminatorColumn = true;
}
else {
LOG.debug( "Ignoring implicit (absent) DiscriminatorColumn" );
generateDiscriminatorColumn = false;
}
}
if ( generateDiscriminatorColumn ) {
final DiscriminatorType discriminatorType = discriminatorColumnAnnotation != null
? discriminatorColumnAnnotation.discriminatorType()
: DiscriminatorType.STRING;
return Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
discriminatorType,
discriminatorColumnAnnotation,
null,
context
);
}
}
else {
if ( discriminatorColumnAnnotation != null ) {
LOG.invalidDiscriminatorAnnotation( clazzToProcess.getName() );
}
}
return null;
}
private static void processIdPropertiesIfNotAlready(
Map<XClass, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext context,
PersistentClass persistentClass,
EntityBinder entityBinder,
PropertyHolder propertyHolder,
HashMap<String, IdentifierGeneratorDefinition> classGenerators,
InheritanceState.ElementsToProcess elementsToProcess,
boolean subclassAndSingleTableStrategy,
Set<String> idPropertiesIfIdClass) {
Set<String> missingIdProperties = new HashSet<>( idPropertiesIfIdClass );
for ( PropertyData propertyAnnotatedElement : elementsToProcess.getElements() ) {
String propertyName = propertyAnnotatedElement.getPropertyName();
if ( !idPropertiesIfIdClass.contains( propertyName ) ) {
processElementAnnotations(
propertyHolder,
subclassAndSingleTableStrategy
? Nullability.FORCED_NULL
: Nullability.NO_CONSTRAINT,
propertyAnnotatedElement,
classGenerators,
entityBinder,
false,
false,
false,
context,
inheritanceStatePerClass
);
}
else {
missingIdProperties.remove( propertyName );
}
}
if ( missingIdProperties.size() != 0 ) {
StringBuilder missings = new StringBuilder();
for ( String property : missingIdProperties ) {
missings.append( property ).append( ", " );
}
throw new AnnotationException(
"Unable to find properties ("
+ missings.substring( 0, missings.length() - 2 )
+ ") in entity annotated with @IdClass:" + persistentClass.getEntityName()
);
}
}
private static boolean mapAsIdClass(
Map<XClass, InheritanceState> inheritanceStatePerClass,
InheritanceState inheritanceState,
PersistentClass persistentClass,
EntityBinder entityBinder,
PropertyHolder propertyHolder,
InheritanceState.ElementsToProcess elementsToProcess,
Set<String> idPropertiesIfIdClass,
MetadataBuildingContext context) {
XClass classWithIdClass = inheritanceState.getClassWithIdClass( false );
if ( classWithIdClass != null ) {
IdClass idClass = classWithIdClass.getAnnotation( IdClass.class );
XClass compositeClass = context.getBootstrapContext().getReflectionManager().toXClass( idClass.value() );
PropertyData inferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", compositeClass
);
PropertyData baseInferredData = new PropertyPreloadedData(
entityBinder.getPropertyAccessType(), "id", classWithIdClass
);
AccessType propertyAccessor = entityBinder.getPropertyAccessor( compositeClass );
final boolean isFakeIdClass = isIdClassPkOfTheAssociatedEntity(
elementsToProcess,
compositeClass,
inferredData,
baseInferredData,
propertyAccessor,
inheritanceStatePerClass,
context
);
if ( isFakeIdClass ) {
return false;
}
boolean isComponent = true;
String generatorType = "assigned";
String generator = BinderHelper.ANNOTATION_STRING_DEFAULT;
boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
entityBinder.setIgnoreIdAnnotations( true );
propertyHolder.setInIdClass( true );
bindIdClass(
generatorType,
generator,
inferredData,
baseInferredData,
null,
propertyHolder,
isComponent,
propertyAccessor,
entityBinder,
true,
false,
context,
inheritanceStatePerClass
);
propertyHolder.setInIdClass( null );
inferredData = new PropertyPreloadedData(
propertyAccessor, PropertyPath.IDENTIFIER_MAPPER_PROPERTY, compositeClass
);
Component mapper = fillComponent(
propertyHolder,
inferredData,
baseInferredData,
propertyAccessor,
false,
entityBinder,
true,
true,
false,
context,
inheritanceStatePerClass
);
entityBinder.setIgnoreIdAnnotations( ignoreIdAnnotations );
persistentClass.setIdentifierMapper( mapper );
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(
classWithIdClass,
inheritanceStatePerClass,
context
);
if ( superclass != null ) {
superclass.setDeclaredIdentifierMapper( mapper );
}
else {
persistentClass.setDeclaredIdentifierMapper( mapper );
}
Property property = new Property();
property.setName( PropertyPath.IDENTIFIER_MAPPER_PROPERTY );
property.setUpdateable( false );
property.setInsertable( false );
property.setValue( mapper );
property.setPropertyAccessorName( "embedded" );
persistentClass.addProperty( property );
entityBinder.setIgnoreIdAnnotations( true );
Iterator properties = mapper.getPropertyIterator();
while ( properties.hasNext() ) {
idPropertiesIfIdClass.add( ( ( Property ) properties.next() ).getName() );
}
return true;
}
else {
return false;
}
}
private static boolean isIdClassPkOfTheAssociatedEntity(
InheritanceState.ElementsToProcess elementsToProcess,
XClass compositeClass,
PropertyData inferredData,
PropertyData baseInferredData,
AccessType propertyAccessor,
Map<XClass, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext context) {
if ( elementsToProcess.getIdPropertyCount() == 1 ) {
final PropertyData idPropertyOnBaseClass = getUniqueIdPropertyFromBaseClass(
inferredData,
baseInferredData,
propertyAccessor,
context
);
final InheritanceState state = inheritanceStatePerClass.get( idPropertyOnBaseClass.getClassOrElement() );
if ( state == null ) {
return false;
}
final XClass associatedClassWithIdClass = state.getClassWithIdClass( true );
if ( associatedClassWithIdClass == null ) {
final XProperty property = idPropertyOnBaseClass.getProperty();
return property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class );
}
else {
final XClass idClass = context.getBootstrapContext().getReflectionManager().toXClass(
associatedClassWithIdClass.getAnnotation( IdClass.class ).value()
);
return idClass.equals( compositeClass );
}
}
else {
return false;
}
}
private static void applyCacheSettings(EntityBinder binder, XClass clazzToProcess, MetadataBuildingContext context) {
binder.applyCaching(
clazzToProcess,
determineSharedCacheMode( context ),
context
);
}
private static SharedCacheMode determineSharedCacheMode(MetadataBuildingContext context) {
return context.getBuildingOptions().getSharedCacheMode();
}
private static PersistentClass makePersistentClass(
InheritanceState inheritanceState,
PersistentClass superEntity,
MetadataBuildingContext metadataBuildingContext) {
if ( !inheritanceState.hasParents() ) {
return new RootClass( metadataBuildingContext );
}
else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
return new SingleTableSubclass( superEntity, metadataBuildingContext );
}
else if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
return new JoinedSubclass( superEntity, metadataBuildingContext );
}
else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.getType() ) ) {
return new UnionSubclass( superEntity, metadataBuildingContext );
}
else {
throw new AssertionFailure( "Unknown inheritance type: " + inheritanceState.getType() );
}
}
private static Ejb3JoinColumn[] makeInheritanceJoinColumns(
XClass clazzToProcess,
MetadataBuildingContext context,
InheritanceState inheritanceState,
PersistentClass superEntity) {
Ejb3JoinColumn[] inheritanceJoinedColumns = null;
final boolean hasJoinedColumns = inheritanceState.hasParents()
&& InheritanceType.JOINED.equals( inheritanceState.getType() );
if ( hasJoinedColumns ) {
PrimaryKeyJoinColumns jcsAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class );
boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
if ( explicitInheritanceJoinedColumns ) {
int nbrOfInhJoinedColumns = jcsAnn.value().length;
PrimaryKeyJoinColumn jcAnn;
inheritanceJoinedColumns = new Ejb3JoinColumn[nbrOfInhJoinedColumns];
for ( int colIndex = 0; colIndex < nbrOfInhJoinedColumns; colIndex++ ) {
jcAnn = jcsAnn.value()[colIndex];
inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
jcAnn,
null,
superEntity.getIdentifier(),
null,
null,
context
);
}
}
else {
PrimaryKeyJoinColumn jcAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class );
inheritanceJoinedColumns = new Ejb3JoinColumn[1];
inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(
jcAnn,
null,
superEntity.getIdentifier(),
null,
null,
context
);
}
LOG.trace( "Subclass joined column(s) created" );
}
else {
if ( clazzToProcess.isAnnotationPresent( PrimaryKeyJoinColumns.class )
|| clazzToProcess.isAnnotationPresent( PrimaryKeyJoinColumn.class ) ) {
LOG.invalidPrimaryKeyJoinColumnAnnotation( clazzToProcess.getName() );
}
}
return inheritanceJoinedColumns;
}
private static PersistentClass getSuperEntity(
XClass clazzToProcess,
Map<XClass, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext context,
InheritanceState inheritanceState) {
InheritanceState superEntityState = InheritanceState.getInheritanceStateOfSuperEntity(
clazzToProcess, inheritanceStatePerClass
);
PersistentClass superEntity = superEntityState != null
? context.getMetadataCollector().getEntityBinding( superEntityState.getClazz().getName() )
: null;
if ( superEntity == null ) {
if ( inheritanceState.hasParents() ) {
throw new AssertionFailure(
"Subclass has to be binded after it's mother class: "
+ superEntityState.getClazz().getName()
);
}
}
return superEntity;
}
private static boolean isEntityClassType(XClass clazzToProcess, AnnotatedClassType classType) {
if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType )
|| AnnotatedClassType.NONE.equals( classType )
|| AnnotatedClassType.EMBEDDABLE.equals( classType )
) {
if ( AnnotatedClassType.NONE.equals( classType )
&& clazzToProcess.isAnnotationPresent( org.hibernate.annotations.Entity.class ) ) {
LOG.missingEntityAnnotation( clazzToProcess.getName() );
}
return false;
}
if ( !classType.equals( AnnotatedClassType.ENTITY ) ) {
throw new AnnotationException(
"Annotated class should have a @javax.persistence.Entity, @javax.persistence.Embeddable or @javax.persistence.EmbeddedSuperclass annotation: " + clazzToProcess
.getName()
);
}
return true;
}
private static void bindFilters(
XClass annotatedClass,
EntityBinder entityBinder,
MetadataBuildingContext context) {
bindFilters( annotatedClass, entityBinder );
XClass classToProcess = annotatedClass.getSuperclass();
while ( classToProcess != null ) {
AnnotatedClassType classType = context.getMetadataCollector().getClassType( classToProcess );
if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) ) {
bindFilters( classToProcess, entityBinder );
}
else {
break;
}
classToProcess = classToProcess.getSuperclass();
}
}
private static void bindFilters(XAnnotatedElement annotatedElement, EntityBinder entityBinder) {
Filters filtersAnn = annotatedElement.getAnnotation( Filters.class );
if ( filtersAnn != null ) {
for ( Filter filter : filtersAnn.value() ) {
entityBinder.addFilter(filter);
}
}
Filter filterAnn = annotatedElement.getAnnotation( Filter.class );
if ( filterAnn != null ) {
entityBinder.addFilter(filterAnn);
}
}
private static void bindFilterDefs(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
FilterDef defAnn = annotatedElement.getAnnotation( FilterDef.class );
FilterDefs defsAnn = annotatedElement.getAnnotation( FilterDefs.class );
if ( defAnn != null ) {
bindFilterDef( defAnn, context );
}
if ( defsAnn != null ) {
for ( FilterDef def : defsAnn.value() ) {
bindFilterDef( def, context );
}
}
}
private static void bindFilterDef(FilterDef defAnn, MetadataBuildingContext context) {
Map<String, org.hibernate.type.Type> params = new HashMap<>();
for ( ParamDef param : defAnn.parameters() ) {
params.put( param.name(), context.getMetadataCollector().getTypeResolver().heuristicType( param.type() ) );
}
FilterDefinition def = new FilterDefinition( defAnn.name(), defAnn.defaultCondition(), params );
LOG.debugf( "Binding filter definition: %s", def.getFilterName() );
context.getMetadataCollector().addFilterDefinition( def );
}
private static void bindTypeDefs(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
TypeDef defAnn = annotatedElement.getAnnotation( TypeDef.class );
TypeDefs defsAnn = annotatedElement.getAnnotation( TypeDefs.class );
if ( defAnn != null ) {
bindTypeDef( defAnn, context );
}
if ( defsAnn != null ) {
for ( TypeDef def : defsAnn.value() ) {
bindTypeDef( def, context );
}
}
}
private static void bindTypeDef(TypeDef defAnn, MetadataBuildingContext context) {
Properties params = new Properties();
for ( Parameter param : defAnn.parameters() ) {
params.setProperty( param.name(), param.value() );
}
if ( BinderHelper.isEmptyAnnotationValue( defAnn.name() ) && defAnn.defaultForType().equals( void.class ) ) {
throw new AnnotationException(
"Either name or defaultForType (or both) attribute should be set in TypeDef having typeClass " +
defAnn.typeClass().getName()
);
}
final String typeBindMessageF = "Binding type definition: %s";
if ( !BinderHelper.isEmptyAnnotationValue( defAnn.name() ) ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( typeBindMessageF, defAnn.name() );
}
context.getMetadataCollector().addTypeDefinition(
new TypeDefinition(
defAnn.name(),
defAnn.typeClass(),
null,
params
)
);
}
if ( !defAnn.defaultForType().equals( void.class ) ) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( typeBindMessageF, defAnn.defaultForType().getName() );
}
context.getMetadataCollector().addTypeDefinition(
new TypeDefinition(
defAnn.defaultForType().getName(),
defAnn.typeClass(),
new String[]{ defAnn.defaultForType().getName() },
params
)
);
}
}
private static void bindFetchProfiles(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
FetchProfile fetchProfileAnnotation = annotatedElement.getAnnotation( FetchProfile.class );
FetchProfiles fetchProfileAnnotations = annotatedElement.getAnnotation( FetchProfiles.class );
if ( fetchProfileAnnotation != null ) {
bindFetchProfile( fetchProfileAnnotation, context );
}
if ( fetchProfileAnnotations != null ) {
for ( FetchProfile profile : fetchProfileAnnotations.value() ) {
bindFetchProfile( profile, context );
}
}
}
private static void bindFetchProfile(FetchProfile fetchProfileAnnotation, MetadataBuildingContext context) {
for ( FetchProfile.FetchOverride fetch : fetchProfileAnnotation.fetchOverrides() ) {
org.hibernate.annotations.FetchMode mode = fetch.mode();
if ( !mode.equals( org.hibernate.annotations.FetchMode.JOIN ) ) {
throw new MappingException( "Only FetchMode.JOIN is currently supported" );
}
context.getMetadataCollector().addSecondPass(
new VerifyFetchProfileReferenceSecondPass(
fetchProfileAnnotation.name(),
fetch,
context
)
);
}
}
private static void bindDiscriminatorColumnToRootPersistentClass(
RootClass rootClass,
Ejb3DiscriminatorColumn discriminatorColumn,
Map<String, Join> secondaryTables,
PropertyHolder propertyHolder,
MetadataBuildingContext context) {
if ( rootClass.getDiscriminator() == null ) {
if ( discriminatorColumn == null ) {
throw new AssertionFailure( "discriminator column should have been built" );
}
discriminatorColumn.setJoins( secondaryTables );
discriminatorColumn.setPropertyHolder( propertyHolder );
SimpleValue discriminatorColumnBinding = new SimpleValue( context, rootClass.getTable() );
rootClass.setDiscriminator( discriminatorColumnBinding );
discriminatorColumn.linkWithValue( discriminatorColumnBinding );
discriminatorColumnBinding.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
rootClass.setPolymorphic( true );
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Setting discriminator for entity {0}", rootClass.getEntityName() );
}
}
}
static int addElementsOfClass(
List<PropertyData> elements,
PropertyContainer propertyContainer,
MetadataBuildingContext context) {
int idPropertyCounter = 0;
Collection<XProperty> properties = propertyContainer.getProperties();
for ( XProperty p : properties ) {
final int currentIdPropertyCounter = addProperty(
propertyContainer,
p,
elements,
context
);
idPropertyCounter += currentIdPropertyCounter;
}
return idPropertyCounter;
}
private static int addProperty(
PropertyContainer propertyContainer,
XProperty property,
List<PropertyData> inFlightPropertyDataList,
MetadataBuildingContext context) {
for ( PropertyData propertyData : inFlightPropertyDataList ) {
if ( propertyData.getPropertyName().equals( property.getName() ) ) {
return 0;
}
}
final XClass declaringClass = propertyContainer.getDeclaringClass();
final XClass entity = propertyContainer.getEntityAtStake();
int idPropertyCounter = 0;
PropertyData propertyAnnotatedElement = new PropertyInferredData(
declaringClass,
property,
propertyContainer.getClassLevelAccessType().getType(),
context.getBootstrapContext().getReflectionManager()
);
final XAnnotatedElement element = propertyAnnotatedElement.getProperty();
if ( element.isAnnotationPresent( Id.class ) || element.isAnnotationPresent( EmbeddedId.class ) ) {
inFlightPropertyDataList.add( 0, propertyAnnotatedElement );
if ( context.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) {
if ( element.isAnnotationPresent( Id.class ) && element.isAnnotationPresent( Column.class ) ) {
String columnName = element.getAnnotation( Column.class ).name();
for ( XProperty prop : declaringClass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
if ( !prop.isAnnotationPresent( MapsId.class ) ) {
boolean isRequiredAnnotationPresent = false;
JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class );
if ( (prop.isAnnotationPresent( JoinColumn.class )
&& prop.getAnnotation( JoinColumn.class ).name().equals( columnName )) ) {
isRequiredAnnotationPresent = true;
}
else if ( prop.isAnnotationPresent( JoinColumns.class ) ) {
for ( JoinColumn columnAnnotation : groupAnnotation.value() ) {
if ( columnName.equals( columnAnnotation.name() ) ) {
isRequiredAnnotationPresent = true;
break;
}
}
}
if ( isRequiredAnnotationPresent ) {
PropertyData specJPropertyData = new PropertyInferredData(
declaringClass,
prop,
propertyContainer.getClassLevelAccessType().getType(),
context.getBootstrapContext().getReflectionManager()
);
context.getMetadataCollector().addPropertyAnnotatedWithMapsIdSpecj(
entity,
specJPropertyData,
element.toString()
);
}
}
}
}
}
if ( element.isAnnotationPresent( ManyToOne.class ) || element.isAnnotationPresent( OneToOne.class ) ) {
context.getMetadataCollector().addToOneAndIdProperty( entity, propertyAnnotatedElement );
}
idPropertyCounter++;
}
else {
inFlightPropertyDataList.add( propertyAnnotatedElement );
}
if ( element.isAnnotationPresent( MapsId.class ) ) {
context.getMetadataCollector().addPropertyAnnotatedWithMapsId( entity, propertyAnnotatedElement );
}
return idPropertyCounter;
}
private static void processElementAnnotations(
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
HashMap<String, IdentifierGeneratorDefinition> classGenerators,
EntityBinder entityBinder,
boolean isIdentifierMapper,
boolean isComponentEmbedded,
boolean inSecondPass,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) throws MappingException {
if ( !propertyHolder.isComponent() ) {
if ( entityBinder.isPropertyDefinedInSuperHierarchy( inferredData.getPropertyName() ) ) {
LOG.debugf(
"Skipping attribute [%s : %s] as it was already processed as part of super hierarchy",
inferredData.getClassOrElementName(),
inferredData.getPropertyName()
);
return;
}
}
final boolean traceEnabled = LOG.isTraceEnabled();
if ( traceEnabled ) {
LOG.tracev( "Processing annotations of {0}.{1}" , propertyHolder.getEntityName(), inferredData.getPropertyName() );
}
final XProperty property = inferredData.getProperty();
if ( property.isAnnotationPresent( Parent.class ) ) {
if ( propertyHolder.isComponent() ) {
propertyHolder.setParentProperty( property.getName() );
}
else {
throw new AnnotationException(
"@Parent cannot be applied outside an embeddable object: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
return;
}
ColumnsBuilder columnsBuilder = new ColumnsBuilder(
propertyHolder,
nullability,
property,
inferredData,
entityBinder,
context
).extractMetadata();
Ejb3Column[] columns = columnsBuilder.getColumns();
Ejb3JoinColumn[] joinColumns = columnsBuilder.getJoinColumns();
final XClass returnedClass = inferredData.getClassOrElement();
PropertyBinder propertyBinder = new PropertyBinder();
propertyBinder.setName( inferredData.getPropertyName() );
propertyBinder.setReturnedClassName( inferredData.getTypeName() );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setHolder( propertyHolder );
propertyBinder.setProperty( property );
propertyBinder.setReturnedClass( inferredData.getPropertyClass() );
propertyBinder.setBuildingContext( context );
if ( isIdentifierMapper ) {
propertyBinder.setInsertable( false );
propertyBinder.setUpdatable( false );
}
propertyBinder.setDeclaringClass( inferredData.getDeclaringClass() );
propertyBinder.setEntityBinder( entityBinder );
propertyBinder.setInheritanceStatePerClass( inheritanceStatePerClass );
boolean isId = !entityBinder.isIgnoreIdAnnotations() &&
( property.isAnnotationPresent( Id.class )
|| property.isAnnotationPresent( EmbeddedId.class ) );
propertyBinder.setId( isId );
final LazyGroup lazyGroupAnnotation = property.getAnnotation( LazyGroup.class );
if ( lazyGroupAnnotation != null ) {
propertyBinder.setLazyGroup( lazyGroupAnnotation.value() );
}
if ( property.isAnnotationPresent( Version.class ) ) {
if ( isIdentifierMapper ) {
throw new AnnotationException(
"@IdClass class should not have @Version property"
);
}
if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) {
throw new AnnotationException(
"Unable to define/override @Version on a subclass: "
+ propertyHolder.getEntityName()
);
}
if ( !propertyHolder.isEntity() ) {
throw new AnnotationException(
"Unable to define @Version on an embedded class: "
+ propertyHolder.getEntityName()
);
}
if ( traceEnabled ) {
LOG.tracev( "{0} is a version property", inferredData.getPropertyName() );
}
RootClass rootClass = ( RootClass ) propertyHolder.getPersistentClass();
propertyBinder.setColumns( columns );
Property prop = propertyBinder.makePropertyValueAndBind();
setVersionInformation( property, propertyBinder );
rootClass.setVersion( prop );
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(
inferredData.getDeclaringClass(),
inheritanceStatePerClass,
context
);
if ( superclass != null ) {
superclass.setDeclaredVersion( prop );
}
else {
rootClass.setDeclaredVersion( prop );
}
SimpleValue simpleValue = ( SimpleValue ) prop.getValue();
simpleValue.setNullValue( "undefined" );
rootClass.setOptimisticLockStyle( OptimisticLockStyle.VERSION );
if ( traceEnabled ) {
LOG.tracev( "Version name: {0}, unsavedValue: {1}", rootClass.getVersion().getName(),
( (SimpleValue) rootClass.getVersion().getValue() ).getNullValue() );
}
}
else {
final boolean forcePersist = property.isAnnotationPresent( MapsId.class )
|| property.isAnnotationPresent( Id.class );
if ( property.isAnnotationPresent( ManyToOne.class ) ) {
ManyToOne ann = property.getAnnotation( ManyToOne.class );
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException(
"@Column(s) not allowed on a @ManyToOne property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
final boolean mandatory = !ann.optional() || forcePersist;
bindManyToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, false, forcePersist ),
joinColumns,
!mandatory,
ignoreNotFound,
onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, context ),
propertyHolder,
inferredData,
false,
isIdentifierMapper,
inSecondPass,
propertyBinder,
context
);
}
else if ( property.isAnnotationPresent( OneToOne.class ) ) {
OneToOne ann = property.getAnnotation( OneToOne.class );
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException(
"@Column(s) not allowed on a @OneToOne property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
boolean trueOneToOne = property.isAnnotationPresent( PrimaryKeyJoinColumn.class )
|| property.isAnnotationPresent( PrimaryKeyJoinColumns.class );
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
final boolean mandatory = !ann.optional() || forcePersist;
bindOneToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, ann.orphanRemoval(), forcePersist ),
joinColumns,
!mandatory,
getFetchMode( ann.fetch() ),
ignoreNotFound, onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, context ),
propertyHolder,
inferredData,
ann.mappedBy(),
trueOneToOne,
isIdentifierMapper,
inSecondPass,
propertyBinder,
context
);
}
else if ( property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException(
"@Column(s) not allowed on a @Any property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
bindAny(
getCascadeStrategy( null, hibernateCascade, false, forcePersist ),
joinColumns,
onDeleteCascade,
nullability,
propertyHolder,
inferredData,
entityBinder,
isIdentifierMapper,
context
);
}
else if ( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( ManyToMany.class )
|| property.isAnnotationPresent( ElementCollection.class )
|| property.isAnnotationPresent( ManyToAny.class ) ) {
OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
ManyToMany manyToManyAnn = property.getAnnotation( ManyToMany.class );
ElementCollection elementCollectionAnn = property.getAnnotation( ElementCollection.class );
if ( ( oneToManyAnn != null || manyToManyAnn != null || elementCollectionAnn != null ) &&
isToManyAssociationWithinEmbeddableCollection(
propertyHolder ) ) {
throw new AnnotationException(
"@OneToMany, @ManyToMany or @ElementCollection cannot be used inside an @Embeddable that is also contained within an @ElementCollection: "
+ BinderHelper.getPath(
propertyHolder,
inferredData
)
);
}
final IndexColumn indexColumn;
if ( property.isAnnotationPresent( OrderColumn.class ) ) {
indexColumn = IndexColumn.buildColumnFromAnnotation(
property.getAnnotation( OrderColumn.class ),
propertyHolder,
inferredData,
entityBinder.getSecondaryTables(),
context
);
if ( property.isAnnotationPresent( ListIndexBase.class ) ) {
indexColumn.setBase( ( property.getAnnotation( ListIndexBase.class ) ).value() );
}
}
else {
indexColumn = IndexColumn.buildColumnFromAnnotation(
property.getAnnotation( org.hibernate.annotations.IndexColumn.class ),
propertyHolder,
inferredData,
context
);
}
CollectionBinder collectionBinder = CollectionBinder.getCollectionBinder(
propertyHolder.getEntityName(),
property,
!indexColumn.isImplicit(),
property.isAnnotationPresent( MapKeyType.class ),
context
);
collectionBinder.setIndexColumn( indexColumn );
collectionBinder.setMapKey( property.getAnnotation( MapKey.class ) );
collectionBinder.setPropertyName( inferredData.getPropertyName() );
collectionBinder.setBatchSize( property.getAnnotation( BatchSize.class ) );
collectionBinder.setJpaOrderBy( property.getAnnotation( javax.persistence.OrderBy.class ) );
collectionBinder.setSqlOrderBy( property.getAnnotation( OrderBy.class ) );
collectionBinder.setSort( property.getAnnotation( Sort.class ) );
collectionBinder.setNaturalSort( property.getAnnotation( SortNatural.class ) );
collectionBinder.setComparatorSort( property.getAnnotation( SortComparator.class ) );
Cache cachAnn = property.getAnnotation( Cache.class );
collectionBinder.setCache( cachAnn );
collectionBinder.setPropertyHolder( propertyHolder );
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
collectionBinder.setIgnoreNotFound( ignoreNotFound );
collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() );
collectionBinder.setBuildingContext( context );
collectionBinder.setAccessType( inferredData.getDefaultAccess() );
Ejb3Column[] elementColumns;
boolean isJPA2ForValueMapping = property.isAnnotationPresent( ElementCollection.class );
PropertyData virtualProperty = isJPA2ForValueMapping ? inferredData : new WrappedInferredData(
inferredData, "element"
);
if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent(
Formula.class
) ) {
Column ann = property.getAnnotation( Column.class );
Formula formulaAnn = property.getAnnotation( Formula.class );
elementColumns = Ejb3Column.buildColumnFromAnnotation(
new Column[] { ann },
formulaAnn,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
context
);
}
else if ( property.isAnnotationPresent( Columns.class ) ) {
Columns anns = property.getAnnotation( Columns.class );
elementColumns = Ejb3Column.buildColumnFromAnnotation(
anns.columns(),
null,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
context
);
}
else {
elementColumns = Ejb3Column.buildColumnFromAnnotation(
null,
null,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
context
);
}
{
Column[] keyColumns = null;
Boolean isJPA2 = null;
if ( property.isAnnotationPresent( MapKeyColumn.class ) ) {
isJPA2 = Boolean.TRUE;
keyColumns = new Column[] { new MapKeyColumnDelegator( property.getAnnotation( MapKeyColumn.class ) ) };
}
if ( isJPA2 == null ) {
isJPA2 = Boolean.TRUE;
}
keyColumns = keyColumns != null && keyColumns.length > 0 ? keyColumns : null;
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
keyColumns,
null,
Nullability.FORCED_NOT_NULL,
propertyHolder,
isJPA2 ? inferredData : mapKeyVirtualProperty,
isJPA2 ? "_KEY" : null,
entityBinder.getSecondaryTables(),
context
);
collectionBinder.setMapKeyColumns( mapColumns );
}
{
JoinColumn[] joinKeyColumns = null;
Boolean isJPA2 = null;
if ( property.isAnnotationPresent( MapKeyJoinColumns.class ) ) {
isJPA2 = Boolean.TRUE;
final MapKeyJoinColumn[] mapKeyJoinColumns = property.getAnnotation( MapKeyJoinColumns.class )
.value();
joinKeyColumns = new JoinColumn[mapKeyJoinColumns.length];
int index = 0;
for ( MapKeyJoinColumn joinColumn : mapKeyJoinColumns ) {
joinKeyColumns[index] = new MapKeyJoinColumnDelegator( joinColumn );
index++;
}
if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
throw new AnnotationException(
"@MapKeyJoinColumn and @MapKeyJoinColumns used on the same property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
}
else if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
isJPA2 = Boolean.TRUE;
joinKeyColumns = new JoinColumn[] {
new MapKeyJoinColumnDelegator(
property.getAnnotation(
MapKeyJoinColumn.class
)
)
};
}
if ( isJPA2 == null ) {
isJPA2 = Boolean.TRUE;
}
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumnsWithDefaultColumnSuffix(
joinKeyColumns,
null,
entityBinder.getSecondaryTables(),
propertyHolder,
isJPA2 ? inferredData.getPropertyName() : mapKeyVirtualProperty.getPropertyName(),
isJPA2 ? "_KEY" : null,
context
);
collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
}
collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
collectionBinder.setElementColumns( elementColumns );
collectionBinder.setProperty( property );
if ( oneToManyAnn != null && manyToManyAnn != null ) {
throw new AnnotationException(
"@OneToMany and @ManyToMany on the same property is not allowed: "
+ propertyHolder.getEntityName() + "." + inferredData.getPropertyName()
);
}
String mappedBy = null;
if ( oneToManyAnn != null ) {
for ( Ejb3JoinColumn column : joinColumns ) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = oneToManyAnn.mappedBy();
collectionBinder.setTargetEntity(
context.getBootstrapContext().getReflectionManager().toXClass( oneToManyAnn.targetEntity() )
);
collectionBinder.setCascadeStrategy(
getCascadeStrategy(
oneToManyAnn.cascade(), hibernateCascade, oneToManyAnn.orphanRemoval(), false
)
);
collectionBinder.setOneToMany( true );
}
else if ( elementCollectionAnn != null ) {
for ( Ejb3JoinColumn column : joinColumns ) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = "";
final Class<?> targetElement = elementCollectionAnn.targetClass();
collectionBinder.setTargetEntity(
context.getBootstrapContext().getReflectionManager().toXClass( targetElement )
);
collectionBinder.setOneToMany( true );
}
else if ( manyToManyAnn != null ) {
mappedBy = manyToManyAnn.mappedBy();
collectionBinder.setTargetEntity(
context.getBootstrapContext().getReflectionManager().toXClass( manyToManyAnn.targetEntity() )
);
collectionBinder.setCascadeStrategy(
getCascadeStrategy(
manyToManyAnn.cascade(), hibernateCascade, false, false
)
);
collectionBinder.setOneToMany( false );
}
else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
mappedBy = "";
collectionBinder.setTargetEntity(
context.getBootstrapContext().getReflectionManager().toXClass( void.class )
);
collectionBinder.setCascadeStrategy( getCascadeStrategy( null, hibernateCascade, false, false ) );
collectionBinder.setOneToMany( false );
}
collectionBinder.setMappedBy( mappedBy );
bindJoinedTableAssociation(
property,
context,
entityBinder,
collectionBinder,
propertyHolder,
inferredData,
mappedBy
);
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
collectionBinder.setCascadeDeleteEnabled( onDeleteCascade );
if ( isIdentifierMapper ) {
collectionBinder.setInsertable( false );
collectionBinder.setUpdatable( false );
}
if ( property.isAnnotationPresent( CollectionId.class ) ) {
HashMap<String, IdentifierGeneratorDefinition> localGenerators = ( HashMap<String, IdentifierGeneratorDefinition> ) classGenerators.clone();
localGenerators.putAll( buildGenerators( property, context ) );
collectionBinder.setLocalGenerators( localGenerators );
}
collectionBinder.setInheritanceStatePerClass( inheritanceStatePerClass );
collectionBinder.setDeclaringClass( inferredData.getDeclaringClass() );
collectionBinder.bind();
}
else if ( !isId || !entityBinder.isIgnoreIdAnnotations() ) {
boolean isComponent = false;
boolean isOverridden = false;
if ( isId || propertyHolder.isOrWithinEmbeddedId() || propertyHolder.isInIdClass() ) {
final PropertyData overridingProperty = BinderHelper.getPropertyOverriddenByMapperOrMapsId(
isId,
propertyHolder,
property.getName(),
context
);
if ( overridingProperty != null ) {
isOverridden = true;
final InheritanceState state = inheritanceStatePerClass.get( overridingProperty.getClassOrElement() );
if ( state != null ) {
isComponent = isComponent || state.hasIdClassOrEmbeddedId();
}
columns = columnsBuilder.overrideColumnFromMapperOrMapsIdProperty( isId );
}
}
isComponent = isComponent
|| property.isAnnotationPresent( Embedded.class )
|| property.isAnnotationPresent( EmbeddedId.class )
|| returnedClass.isAnnotationPresent( Embeddable.class );
if ( isComponent ) {
String referencedEntityName = null;
if ( isOverridden ) {
final PropertyData mapsIdProperty = BinderHelper.getPropertyOverriddenByMapperOrMapsId(
isId, propertyHolder, property.getName(), context
);
referencedEntityName = mapsIdProperty.getClassOrElementName();
}
AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
propertyBinder = bindComponent(
inferredData,
propertyHolder,
propertyAccessor,
entityBinder,
isIdentifierMapper,
context,
isComponentEmbedded,
isId,
inheritanceStatePerClass,
referencedEntityName,
isOverridden ? ( Ejb3JoinColumn[] ) columns : null
);
}
else {
boolean optional = true;
boolean lazy = false;
if ( property.isAnnotationPresent( Basic.class ) ) {
Basic ann = property.getAnnotation( Basic.class );
optional = ann.optional();
lazy = ann.fetch() == FetchType.LAZY;
}
if ( isId || ( !optional && nullability != Nullability.FORCED_NULL ) ) {
for ( Ejb3Column col : columns ) {
if ( isId && col.isFormula() ) {
throw new CannotForceNonNullableException(
String.format(
Locale.ROOT,
"Identifier property [%s] cannot contain formula mapping [%s]",
HCANNHelper.annotatedElementSignature( property ),
col.getFormulaString()
)
);
}
col.forceNotNull();
}
}
propertyBinder.setLazy( lazy );
propertyBinder.setColumns( columns );
if ( isOverridden ) {
final PropertyData mapsIdProperty = BinderHelper.getPropertyOverriddenByMapperOrMapsId(
isId, propertyHolder, property.getName(), context
);
propertyBinder.setReferencedEntityName( mapsIdProperty.getClassOrElementName() );
}
propertyBinder.makePropertyValueAndBind();
}
if ( isOverridden ) {
final PropertyData mapsIdProperty = BinderHelper.getPropertyOverriddenByMapperOrMapsId(
isId, propertyHolder, property.getName(), context
);
final IdentifierGeneratorDefinition.Builder foreignGeneratorBuilder = new IdentifierGeneratorDefinition.Builder();
foreignGeneratorBuilder.setName( "Hibernate-local--foreign generator" );
foreignGeneratorBuilder.setStrategy( "foreign" );
foreignGeneratorBuilder.addParam( "property", mapsIdProperty.getPropertyName() );
final IdentifierGeneratorDefinition foreignGenerator = foreignGeneratorBuilder.build();
if ( isGlobalGeneratorNameGlobal( context ) ) {
SecondPass secondPass = new IdGeneratorResolverSecondPass(
(SimpleValue) propertyBinder.getValue(),
property,
foreignGenerator.getStrategy(),
foreignGenerator.getName(),
context,
foreignGenerator
);
context.getMetadataCollector().addSecondPass( secondPass );
}
else {
Map<String, IdentifierGeneratorDefinition> localGenerators = (HashMap<String, IdentifierGeneratorDefinition>) classGenerators
.clone();
localGenerators.put( foreignGenerator.getName(), foreignGenerator );
BinderHelper.makeIdGenerator(
(SimpleValue) propertyBinder.getValue(),
property,
foreignGenerator.getStrategy(),
foreignGenerator.getName(),
context,
localGenerators
);
}
}
if ( isId ) {
final SimpleValue value = ( SimpleValue ) propertyBinder.getValue();
if ( !isOverridden ) {
processId(
propertyHolder,
inferredData,
value,
classGenerators,
isIdentifierMapper,
context
);
}
}
}
}
Index index = property.getAnnotation( Index.class );
if ( index != null ) {
if ( joinColumns != null ) {
for ( Ejb3Column column : joinColumns ) {
column.addIndex( index, inSecondPass );
}
}
else {
if ( columns != null ) {
for ( Ejb3Column column : columns ) {
column.addIndex( index, inSecondPass );
}
}
}
}
NaturalId naturalIdAnn = property.getAnnotation( NaturalId.class );
if ( naturalIdAnn != null ) {
if ( joinColumns != null ) {
for ( Ejb3Column column : joinColumns ) {
String keyName = "UK_" + Constraint.hashedName( column.getTable().getName() + "_NaturalID" );
column.addUniqueKey( keyName, inSecondPass );
}
}
else {
for ( Ejb3Column column : columns ) {
String keyName = "UK_" + Constraint.hashedName( column.getTable().getName() + "_NaturalID" );
column.addUniqueKey( keyName, inSecondPass );
}
}
}
}
private static boolean isGlobalGeneratorNameGlobal(MetadataBuildingContext context) {
return context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled();
}
private static boolean isToManyAssociationWithinEmbeddableCollection(PropertyHolder propertyHolder) {
if(propertyHolder instanceof ComponentPropertyHolder) {
ComponentPropertyHolder componentPropertyHolder = (ComponentPropertyHolder) propertyHolder;
return componentPropertyHolder.isWithinElementCollection();
}
return false;
}
private static void setVersionInformation(XProperty property, PropertyBinder propertyBinder) {
propertyBinder.getSimpleValueBinder().setVersion( true );
if(property.isAnnotationPresent( Source.class )) {
Source source = property.getAnnotation( Source.class );
propertyBinder.getSimpleValueBinder().setTimestampVersionType( source.value().typeName() );
}
}
private static void processId(
PropertyHolder propertyHolder,
PropertyData inferredData,
SimpleValue idValue,
HashMap<String, IdentifierGeneratorDefinition> classGenerators,
boolean isIdentifierMapper,
MetadataBuildingContext buildingContext) {
if ( isIdentifierMapper ) {
throw new AnnotationException(
"@IdClass class should not have @Id nor @EmbeddedId properties: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
XClass entityXClass = inferredData.getClassOrElement();
XProperty idXProperty = inferredData.getProperty();
final boolean isComponent = entityXClass.isAnnotationPresent( Embeddable.class )
|| idXProperty.isAnnotationPresent( EmbeddedId.class );
GeneratedValue generatedValue = idXProperty.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null
? generatorType( generatedValue, buildingContext, entityXClass )
: "assigned";
String generatorName = generatedValue != null
? generatedValue.generator()
: BinderHelper.ANNOTATION_STRING_DEFAULT;
if ( isComponent ) {
generatorType = "assigned";
}
if ( isGlobalGeneratorNameGlobal( buildingContext ) ) {
buildGenerators( idXProperty, buildingContext );
SecondPass secondPass = new IdGeneratorResolverSecondPass(
idValue,
idXProperty,
generatorType,
generatorName,
buildingContext
);
buildingContext.getMetadataCollector().addSecondPass( secondPass );
}
else {
HashMap<String, IdentifierGeneratorDefinition> localGenerators = (HashMap<String, IdentifierGeneratorDefinition>) classGenerators
.clone();
localGenerators.putAll( buildGenerators( idXProperty, buildingContext ) );
BinderHelper.makeIdGenerator(
idValue,
idXProperty,
generatorType,
generatorName,
buildingContext,
localGenerators
);
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Bind {0} on {1}", ( isComponent ? "@EmbeddedId" : "@Id" ), inferredData.getPropertyName() );
}
}
public static String generatorType(
GeneratedValue generatedValueAnn,
final MetadataBuildingContext buildingContext,
final XClass javaTypeXClass) {
return buildingContext.getBuildingOptions().getIdGenerationTypeInterpreter().determineGeneratorName(
generatedValueAnn.strategy(),
new IdGeneratorStrategyInterpreter.GeneratorNameDeterminationContext() {
Class javaType = null;
@Override
public Class getIdType() {
if ( javaType == null ) {
javaType = buildingContext
.getBootstrapContext()
.getReflectionManager()
.toClass( javaTypeXClass );
}
return javaType;
}
@Override
public String getGeneratedValueGeneratorName() {
return generatedValueAnn.generator();
}
}
);
}
private static void bindJoinedTableAssociation(
XProperty property,
MetadataBuildingContext buildingContext,
EntityBinder entityBinder,
CollectionBinder collectionBinder,
PropertyHolder propertyHolder,
PropertyData inferredData,
String mappedBy) {
TableBinder associationTableBinder = new TableBinder();
JoinColumn[] annJoins;
JoinColumn[] annInverseJoins;
JoinTable assocTable = propertyHolder.getJoinTable( property );
CollectionTable collectionTable = property.getAnnotation( CollectionTable.class );
if ( assocTable != null || collectionTable != null ) {
final String catalog;
final String schema;
final String tableName;
final UniqueConstraint[] uniqueConstraints;
final JoinColumn[] joins;
final JoinColumn[] inverseJoins;
final javax.persistence.Index[] jpaIndexes;
if ( collectionTable != null ) {
catalog = collectionTable.catalog();
schema = collectionTable.schema();
tableName = collectionTable.name();
uniqueConstraints = collectionTable.uniqueConstraints();
joins = collectionTable.joinColumns();
inverseJoins = null;
jpaIndexes = collectionTable.indexes();
}
else {
catalog = assocTable.catalog();
schema = assocTable.schema();
tableName = assocTable.name();
uniqueConstraints = assocTable.uniqueConstraints();
joins = assocTable.joinColumns();
inverseJoins = assocTable.inverseJoinColumns();
jpaIndexes = assocTable.indexes();
}
collectionBinder.setExplicitAssociationTable( true );
if ( jpaIndexes != null && jpaIndexes.length > 0 ) {
associationTableBinder.setJpaIndex( jpaIndexes );
}
if ( !BinderHelper.isEmptyAnnotationValue( schema ) ) {
associationTableBinder.setSchema( schema );
}
if ( !BinderHelper.isEmptyAnnotationValue( catalog ) ) {
associationTableBinder.setCatalog( catalog );
}
if ( !BinderHelper.isEmptyAnnotationValue( tableName ) ) {
associationTableBinder.setName( tableName );
}
associationTableBinder.setUniqueConstraints( uniqueConstraints );
associationTableBinder.setJpaIndex( jpaIndexes );
annJoins = joins.length == 0 ? null : joins;
annInverseJoins = inverseJoins == null || inverseJoins.length == 0 ? null : inverseJoins;
}
else {
annJoins = null;
annInverseJoins = null;
}
Ejb3JoinColumn[] joinColumns = Ejb3JoinColumn.buildJoinTableJoinColumns(
annJoins,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
mappedBy,
buildingContext
);
Ejb3JoinColumn[] inverseJoinColumns = Ejb3JoinColumn.buildJoinTableJoinColumns(
annInverseJoins,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
mappedBy,
buildingContext
);
associationTableBinder.setBuildingContext( buildingContext );
collectionBinder.setTableBinder( associationTableBinder );
collectionBinder.setJoinColumns( joinColumns );
collectionBinder.setInverseJoinColumns( inverseJoinColumns );
}
private static PropertyBinder bindComponent(
PropertyData inferredData,
PropertyHolder propertyHolder,
AccessType propertyAccessor,
EntityBinder entityBinder,
boolean isIdentifierMapper,
MetadataBuildingContext buildingContext,
boolean isComponentEmbedded,
boolean isId,
Map<XClass, InheritanceState> inheritanceStatePerClass,
String referencedEntityName,
Ejb3JoinColumn[] columns) {
Component comp;
if ( referencedEntityName != null ) {
comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, buildingContext );
SecondPass sp = new CopyIdentifierComponentSecondPass(
comp,
referencedEntityName,
columns,
buildingContext
);
buildingContext.getMetadataCollector().addSecondPass( sp );
}
else {
comp = fillComponent(
propertyHolder, inferredData, propertyAccessor, !isId, entityBinder,
isComponentEmbedded, isIdentifierMapper,
false, buildingContext, inheritanceStatePerClass
);
}
if ( isId ) {
comp.setKey( true );
if ( propertyHolder.getPersistentClass().getIdentifier() != null ) {
throw new AnnotationException(
comp.getComponentClassName()
+ " must not have @Id properties when used as an @EmbeddedId: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
if ( referencedEntityName == null && comp.getPropertySpan() == 0 ) {
throw new AnnotationException(
comp.getComponentClassName()
+ " has no persistent id property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
}
XProperty property = inferredData.getProperty();
setupComponentTuplizer( property, comp );
PropertyBinder binder = new PropertyBinder();
binder.setDeclaringClass(inferredData.getDeclaringClass());
binder.setName( inferredData.getPropertyName() );
binder.setValue( comp );
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( buildingContext );
binder.makePropertyAndBind();
return binder;
}
public static Component fillComponent(
PropertyHolder propertyHolder,
PropertyData inferredData,
AccessType propertyAccessor,
boolean isNullable,
EntityBinder entityBinder,
boolean isComponentEmbedded,
boolean isIdentifierMapper,
boolean inSecondPass,
MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
return fillComponent(
propertyHolder,
inferredData,
null,
propertyAccessor,
isNullable,
entityBinder,
isComponentEmbedded,
isIdentifierMapper,
inSecondPass,
buildingContext,
inheritanceStatePerClass
);
}
public static Component fillComponent(
PropertyHolder propertyHolder,
PropertyData inferredData,
PropertyData baseInferredData,
AccessType propertyAccessor,
boolean isNullable,
EntityBinder entityBinder,
boolean isComponentEmbedded,
boolean isIdentifierMapper,
boolean inSecondPass,
MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
Component comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, buildingContext );
String subpath = BinderHelper.getPath( propertyHolder, inferredData );
LOG.tracev( "Binding component with path: {0}", subpath );
PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(
comp,
subpath,
inferredData,
propertyHolder,
buildingContext
);
propertyHolder.startingProperty( inferredData.getProperty() );
final XClass xClassProcessed = inferredData.getPropertyClass();
List<PropertyData> classElements = new ArrayList<>();
XClass returnedClassOrElement = inferredData.getClassOrElement();
List<PropertyData> baseClassElements = null;
Map<String, PropertyData> orderedBaseClassElements = new HashMap<>();
XClass baseReturnedClassOrElement;
if ( baseInferredData != null ) {
baseClassElements = new ArrayList<>();
baseReturnedClassOrElement = baseInferredData.getClassOrElement();
bindTypeDefs( baseReturnedClassOrElement, buildingContext );
while ( !Object.class.getName().equals( baseReturnedClassOrElement.getName() ) ) {
PropertyContainer propContainer = new PropertyContainer( baseReturnedClassOrElement, xClassProcessed, propertyAccessor );
addElementsOfClass( baseClassElements, propContainer, buildingContext );
for ( PropertyData element : baseClassElements ) {
orderedBaseClassElements.put( element.getPropertyName(), element );
}
baseReturnedClassOrElement = baseReturnedClassOrElement.getSuperclass();
}
}
bindTypeDefs( returnedClassOrElement, buildingContext );
PropertyContainer propContainer = new PropertyContainer( returnedClassOrElement, xClassProcessed, propertyAccessor );
addElementsOfClass( classElements, propContainer, buildingContext );
XClass superClass = xClassProcessed.getSuperclass();
while ( superClass != null && superClass.isAnnotationPresent( MappedSuperclass.class ) ) {
propContainer = new PropertyContainer( superClass, xClassProcessed, propertyAccessor );
addElementsOfClass( classElements, propContainer, buildingContext );
superClass = superClass.getSuperclass();
}
if ( baseClassElements != null ) {
if ( !hasAnnotationsOnIdClass( xClassProcessed ) ) {
for ( int i = 0; i < classElements.size(); i++ ) {
final PropertyData idClassPropertyData = classElements.get( i );
final PropertyData entityPropertyData = orderedBaseClassElements.get( idClassPropertyData.getPropertyName() );
if ( propertyHolder.isInIdClass() ) {
if ( entityPropertyData == null ) {
throw new AnnotationException(
"Property of @IdClass not found in entity "
+ baseInferredData.getPropertyClass().getName() + ": "
+ idClassPropertyData.getPropertyName()
);
}
final boolean hasXToOneAnnotation = entityPropertyData.getProperty()
.isAnnotationPresent( ManyToOne.class )
|| entityPropertyData.getProperty().isAnnotationPresent( OneToOne.class );
final boolean isOfDifferentType = !entityPropertyData.getClassOrElement()
.equals( idClassPropertyData.getClassOrElement() );
if ( hasXToOneAnnotation && isOfDifferentType ) {
}
else {
classElements.set( i, entityPropertyData );
}
}
else {
classElements.set( i, entityPropertyData );
}
}
}
}
for ( PropertyData propertyAnnotatedElement : classElements ) {
processElementAnnotations(
subHolder,
isNullable
? Nullability.NO_CONSTRAINT
: Nullability.FORCED_NOT_NULL,
propertyAnnotatedElement,
new HashMap<>(),
entityBinder,
isIdentifierMapper,
isComponentEmbedded,
inSecondPass,
buildingContext,
inheritanceStatePerClass
);
XProperty property = propertyAnnotatedElement.getProperty();
if ( property.isAnnotationPresent( GeneratedValue.class ) &&
property.isAnnotationPresent( Id.class ) ) {
GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null
? generatorType( generatedValue, buildingContext, property.getType() )
: "assigned";
String generator = generatedValue != null ?
generatedValue.generator() :
BinderHelper.ANNOTATION_STRING_DEFAULT;
if ( isGlobalGeneratorNameGlobal( buildingContext ) ) {
buildGenerators( property, buildingContext );
SecondPass secondPass = new IdGeneratorResolverSecondPass(
(SimpleValue) comp.getProperty( property.getName() ).getValue(),
property,
generatorType,
generator,
buildingContext
);
buildingContext.getMetadataCollector().addSecondPass( secondPass );
}
else {
Map<String, IdentifierGeneratorDefinition> localGenerators = new HashMap<>();
localGenerators.putAll( buildGenerators( property, buildingContext ) );
BinderHelper.makeIdGenerator(
(SimpleValue) comp.getProperty( property.getName() ).getValue(),
property,
generatorType,
generator,
buildingContext,
localGenerators
);
}
}
}
return comp;
}
public static Component createComponent(
PropertyHolder propertyHolder,
PropertyData inferredData,
boolean isComponentEmbedded,
boolean isIdentifierMapper,
MetadataBuildingContext context) {
Component comp = new Component( context, propertyHolder.getPersistentClass() );
comp.setEmbedded( isComponentEmbedded );
comp.setTable( propertyHolder.getTable() );
if ( isIdentifierMapper || ( isComponentEmbedded && inferredData.getPropertyName() == null ) ) {
comp.setComponentClassName( comp.getOwner().getClassName() );
}
else {
comp.setComponentClassName( inferredData.getClassOrElementName() );
}
return comp;
}
private static void bindIdClass(
String generatorType,
String generatorName,
PropertyData inferredData,
PropertyData baseInferredData,
Ejb3Column[] columns,
PropertyHolder propertyHolder,
boolean isComposite,
AccessType propertyAccessor,
EntityBinder entityBinder,
boolean isEmbedded,
boolean isIdentifierMapper,
MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
PersistentClass persistentClass = propertyHolder.getPersistentClass();
if ( !( persistentClass instanceof RootClass ) ) {
throw new AnnotationException(
"Unable to define/override @Id(s) on a subclass: "
+ propertyHolder.getEntityName()
);
}
RootClass rootClass = ( RootClass ) persistentClass;
String persistentClassName = rootClass.getClassName();
SimpleValue id;
final String propertyName = inferredData.getPropertyName();
if ( isComposite ) {
id = fillComponent(
propertyHolder,
inferredData,
baseInferredData,
propertyAccessor,
false,
entityBinder,
isEmbedded,
isIdentifierMapper,
false,
buildingContext,
inheritanceStatePerClass
);
Component componentId = ( Component ) id;
componentId.setKey( true );
if ( rootClass.getIdentifier() != null ) {
throw new AnnotationException( componentId.getComponentClassName() + " must not have @Id properties when used as an @EmbeddedId" );
}
if ( componentId.getPropertySpan() == 0 ) {
throw new AnnotationException( componentId.getComponentClassName() + " has no persistent id property" );
}
XProperty property = inferredData.getProperty();
setupComponentTuplizer( property, componentId );
}
else {
for ( Ejb3Column column : columns ) {
column.forceNotNull();
}
SimpleValueBinder value = new SimpleValueBinder();
value.setPropertyName( propertyName );
value.setReturnedClassName( inferredData.getTypeName() );
value.setColumns( columns );
value.setPersistentClassName( persistentClassName );
value.setBuildingContext( buildingContext );
value.setType( inferredData.getProperty(), inferredData.getClassOrElement(), persistentClassName, null );
value.setAccessType( propertyAccessor );
id = value.make();
}
rootClass.setIdentifier( id );
if ( isGlobalGeneratorNameGlobal( buildingContext ) ) {
SecondPass secondPass = new IdGeneratorResolverSecondPass(
id,
inferredData.getProperty(),
generatorType,
generatorName,
buildingContext
);
buildingContext.getMetadataCollector().addSecondPass( secondPass );
}
else {
BinderHelper.makeIdGenerator(
id,
inferredData.getProperty(),
generatorType,
generatorName,
buildingContext,
Collections.emptyMap()
);
}
if ( isEmbedded ) {
rootClass.setEmbeddedIdentifier( inferredData.getPropertyClass() == null );
}
else {
PropertyBinder binder = new PropertyBinder();
binder.setName( propertyName );
binder.setValue( id );
binder.setAccessType( inferredData.getDefaultAccess() );
binder.setProperty( inferredData.getProperty() );
Property prop = binder.makeProperty();
rootClass.setIdentifierProperty( prop );
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(
inferredData.getDeclaringClass(),
inheritanceStatePerClass,
buildingContext
);
if ( superclass != null ) {
superclass.setDeclaredIdentifierProperty( prop );
}
else {
rootClass.setDeclaredIdentifierProperty( prop );
}
}
}
private static PropertyData getUniqueIdPropertyFromBaseClass(
PropertyData inferredData,
PropertyData baseInferredData,
AccessType propertyAccessor,
MetadataBuildingContext context) {
List<PropertyData> baseClassElements = new ArrayList<>();
XClass baseReturnedClassOrElement = baseInferredData.getClassOrElement();
PropertyContainer propContainer = new PropertyContainer(
baseReturnedClassOrElement,
inferredData.getPropertyClass(),
propertyAccessor
);
addElementsOfClass( baseClassElements, propContainer, context );
return baseClassElements.get( 0 );
}
private static void setupComponentTuplizer(XProperty property, Component component) {
if ( property == null ) {
return;
}
if ( property.isAnnotationPresent( Tuplizers.class ) ) {
for ( Tuplizer tuplizer : property.getAnnotation( Tuplizers.class ).value() ) {
EntityMode mode = EntityMode.parse( tuplizer.entityMode() );
component.addTuplizer( mode, tuplizer.impl().getName() );
}
}
if ( property.isAnnotationPresent( Tuplizer.class ) ) {
Tuplizer tuplizer = property.getAnnotation( Tuplizer.class );
EntityMode mode = EntityMode.parse( tuplizer.entityMode() );
component.addTuplizer( mode, tuplizer.impl().getName() );
}
}
private static void bindManyToOne(
String cascadeStrategy,
Ejb3JoinColumn[] columns,
boolean optional,
boolean ignoreNotFound,
boolean cascadeOnDelete,
XClass targetEntity,
PropertyHolder propertyHolder,
PropertyData inferredData,
boolean unique,
boolean isIdentifierMapper,
boolean inSecondPass,
PropertyBinder propertyBinder,
MetadataBuildingContext context) {
org.hibernate.mapping.ManyToOne value = new org.hibernate.mapping.ManyToOne( context, columns[0].getTable() );
if ( unique ) {
value.markAsLogicalOneToOne();
}
value.setReferencedEntityName( ToOneBinder.getReferenceEntityName( inferredData, targetEntity, context ) );
final XProperty property = inferredData.getProperty();
defineFetchingStrategy( value, property );
value.setIgnoreNotFound( ignoreNotFound );
value.setCascadeDeleteEnabled( cascadeOnDelete );
if ( !optional ) {
for ( Ejb3JoinColumn column : columns ) {
column.setNullable( false );
}
}
if ( property.isAnnotationPresent( MapsId.class ) ) {
for ( Ejb3JoinColumn column : columns ) {
column.setInsertable( false );
column.setUpdatable( false );
}
}
final JoinColumn joinColumn = property.getAnnotation( JoinColumn.class );
final JoinColumns joinColumns = property.getAnnotation( JoinColumns.class );
boolean hasSpecjManyToOne=false;
if ( context.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) {
String columnName = "";
for ( XProperty prop : inferredData.getDeclaringClass()
.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
if ( prop.isAnnotationPresent( Id.class ) && prop.isAnnotationPresent( Column.class ) ) {
columnName = prop.getAnnotation( Column.class ).name();
}
if ( property.isAnnotationPresent( ManyToOne.class ) && joinColumn != null
&& ! BinderHelper.isEmptyAnnotationValue( joinColumn.name() )
&& joinColumn.name().equals( columnName )
&& !property.isAnnotationPresent( MapsId.class ) ) {
hasSpecjManyToOne = true;
for ( Ejb3JoinColumn column : columns ) {
column.setInsertable( false );
column.setUpdatable( false );
}
}
}
}
value.setTypeName( inferredData.getClassOrElementName() );
final String propertyName = inferredData.getPropertyName();
value.setTypeUsingReflection( propertyHolder.getClassName(), propertyName );
if ( ( joinColumn != null && joinColumn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT )
|| ( joinColumns != null && joinColumns.foreignKey().value() == ConstraintMode.NO_CONSTRAINT ) ) {
value.setForeignKeyName( "none" );
}
else {
final ForeignKey fk = property.getAnnotation( ForeignKey.class );
if ( fk != null && StringHelper.isNotEmpty( fk.name() ) ) {
value.setForeignKeyName( fk.name() );
}
else {
final javax.persistence.ForeignKey fkOverride = propertyHolder.getOverriddenForeignKey(
StringHelper.qualify( propertyHolder.getPath(), propertyName )
);
if ( fkOverride != null && fkOverride.value() == ConstraintMode.NO_CONSTRAINT ) {
value.setForeignKeyName( "none" );
}
else if ( fkOverride != null ) {
value.setForeignKeyName( StringHelper.nullIfEmpty( fkOverride.name() ) );
value.setForeignKeyDefinition( StringHelper.nullIfEmpty( fkOverride.foreignKeyDefinition() ) );
}
else if ( joinColumns != null ) {
value.setForeignKeyName( StringHelper.nullIfEmpty( joinColumns.foreignKey().name() ) );
value.setForeignKeyDefinition( StringHelper.nullIfEmpty( joinColumns.foreignKey().foreignKeyDefinition() ) );
}
else if ( joinColumn != null ) {
value.setForeignKeyName( StringHelper.nullIfEmpty( joinColumn.foreignKey().name() ) );
value.setForeignKeyDefinition( StringHelper.nullIfEmpty( joinColumn.foreignKey().foreignKeyDefinition() ) );
}
}
}
String path = propertyHolder.getPath() + "." + propertyName;
FkSecondPass secondPass = new ToOneFkSecondPass(
value, columns,
!optional && unique,
propertyHolder.getEntityOwnerClassName(),
path,
context
);
if ( inSecondPass ) {
secondPass.doSecondPass( context.getMetadataCollector().getEntityBindingMap() );
}
else {
context.getMetadataCollector().addSecondPass( secondPass );
}
Ejb3Column.checkPropertyConsistency( columns, propertyHolder.getEntityName() + "." + propertyName );
propertyBinder.setName( propertyName );
propertyBinder.setValue( value );
if ( isIdentifierMapper ) {
propertyBinder.setInsertable( false );
propertyBinder.setUpdatable( false );
}
else if (hasSpecjManyToOne) {
propertyBinder.setInsertable( false );
propertyBinder.setUpdatable( false );
}
else {
propertyBinder.setInsertable( columns[0].isInsertable() );
propertyBinder.setUpdatable( columns[0].isUpdatable() );
}
propertyBinder.setColumns( columns );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setCascade( cascadeStrategy );
propertyBinder.setProperty( property );
propertyBinder.setXToMany( true );
propertyBinder.makePropertyAndBind();
}
protected static void defineFetchingStrategy(ToOne toOne, XProperty property) {
LazyToOne lazy = property.getAnnotation( LazyToOne.class );
Fetch fetch = property.getAnnotation( Fetch.class );
ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
OneToOne oneToOne = property.getAnnotation( OneToOne.class );
FetchType fetchType;
if ( manyToOne != null ) {
fetchType = manyToOne.fetch();
}
else if ( oneToOne != null ) {
fetchType = oneToOne.fetch();
}
else {
throw new AssertionFailure(
"Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne"
);
}
if ( lazy != null ) {
toOne.setLazy( !( lazy.value() == LazyToOneOption.FALSE ) );
toOne.setUnwrapProxy( ( lazy.value() == LazyToOneOption.NO_PROXY ) );
}
else {
toOne.setLazy( fetchType == FetchType.LAZY );
toOne.setUnwrapProxy( false );
}
if ( fetch != null ) {
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
toOne.setFetchMode( FetchMode.JOIN );
toOne.setLazy( false );
toOne.setUnwrapProxy( false );
}
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SELECT ) {
toOne.setFetchMode( FetchMode.SELECT );
}
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
throw new AnnotationException( "Use of FetchMode.SUBSELECT not allowed on ToOne associations" );
}
else {
throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
}
}
else {
toOne.setFetchMode( getFetchMode( fetchType ) );
}
}
private static void bindOneToOne(
String cascadeStrategy,
Ejb3JoinColumn[] joinColumns,
boolean optional,
FetchMode fetchMode,
boolean ignoreNotFound,
boolean cascadeOnDelete,
XClass targetEntity,
PropertyHolder propertyHolder,
PropertyData inferredData,
String mappedBy,
boolean trueOneToOne,
boolean isIdentifierMapper,
boolean inSecondPass,
PropertyBinder propertyBinder,
MetadataBuildingContext context) {
final String propertyName = inferredData.getPropertyName();
LOG.tracev( "Fetching {0} with {1}", propertyName, fetchMode );
boolean mapToPK = true;
if ( !trueOneToOne ) {
KeyValue identifier = propertyHolder.getIdentifier();
if ( identifier == null ) {
mapToPK = false;
}
else {
Iterator idColumns = identifier.getColumnIterator();
List<String> idColumnNames = new ArrayList<>();
org.hibernate.mapping.Column currentColumn;
if ( identifier.getColumnSpan() != joinColumns.length ) {
mapToPK = false;
}
else {
while ( idColumns.hasNext() ) {
currentColumn = ( org.hibernate.mapping.Column ) idColumns.next();
idColumnNames.add( currentColumn.getName() );
}
for ( Ejb3JoinColumn col : joinColumns ) {
if ( !idColumnNames.contains( col.getMappingColumn().getName() ) ) {
mapToPK = false;
break;
}
}
}
}
}
if ( trueOneToOne || mapToPK || !BinderHelper.isEmptyAnnotationValue( mappedBy ) ) {
OneToOneSecondPass secondPass = new OneToOneSecondPass(
mappedBy,
propertyHolder.getEntityName(),
propertyName,
propertyHolder,
inferredData,
targetEntity,
ignoreNotFound,
cascadeOnDelete,
optional,
cascadeStrategy,
joinColumns,
context
);
if ( inSecondPass ) {
secondPass.doSecondPass( context.getMetadataCollector().getEntityBindingMap() );
}
else {
context.getMetadataCollector().addSecondPass(
secondPass,
BinderHelper.isEmptyAnnotationValue( mappedBy )
);
}
}
else {
bindManyToOne(
cascadeStrategy, joinColumns, optional, ignoreNotFound, cascadeOnDelete,
targetEntity,
propertyHolder, inferredData, true, isIdentifierMapper, inSecondPass,
propertyBinder, context
);
}
}
private static void bindAny(
String cascadeStrategy,
Ejb3JoinColumn[] columns,
boolean cascadeOnDelete,
Nullability nullability,
PropertyHolder propertyHolder,
PropertyData inferredData,
EntityBinder entityBinder,
boolean isIdentifierMapper,
MetadataBuildingContext buildingContext) {
org.hibernate.annotations.Any anyAnn = inferredData.getProperty()
.getAnnotation( org.hibernate.annotations.Any.class );
if ( anyAnn == null ) {
throw new AssertionFailure(
"Missing @Any annotation: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
Any value = BinderHelper.buildAnyValue(
anyAnn.metaDef(),
columns,
anyAnn.metaColumn(),
inferredData,
cascadeOnDelete,
nullability,
propertyHolder,
entityBinder,
anyAnn.optional(),
buildingContext
);
PropertyBinder binder = new PropertyBinder();
binder.setName( inferredData.getPropertyName() );
binder.setValue( value );
binder.setLazy( anyAnn.fetch() == FetchType.LAZY );
if ( isIdentifierMapper ) {
binder.setInsertable( false );
binder.setUpdatable( false );
}
else {
binder.setInsertable( columns[0].isInsertable() );
binder.setUpdatable( columns[0].isUpdatable() );
}
binder.setAccessType( inferredData.getDefaultAccess() );
binder.setCascade( cascadeStrategy );
Property prop = binder.makeProperty();
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
}
private static EnumSet<CascadeType> convertToHibernateCascadeType(javax.persistence.CascadeType[] ejbCascades) {
EnumSet<CascadeType> hibernateCascadeSet = EnumSet.noneOf( CascadeType.class );
if ( ejbCascades != null && ejbCascades.length > 0 ) {
for ( javax.persistence.CascadeType cascade : ejbCascades ) {
switch ( cascade ) {
case ALL:
hibernateCascadeSet.add( CascadeType.ALL );
break;
case PERSIST:
hibernateCascadeSet.add( CascadeType.PERSIST );
break;
case MERGE:
hibernateCascadeSet.add( CascadeType.MERGE );
break;
case REMOVE:
hibernateCascadeSet.add( CascadeType.REMOVE );
break;
case REFRESH:
hibernateCascadeSet.add( CascadeType.REFRESH );
break;
case DETACH:
hibernateCascadeSet.add( CascadeType.DETACH );
break;
}
}
}
return hibernateCascadeSet;
}
private static String getCascadeStrategy(
javax.persistence.CascadeType[] ejbCascades,
Cascade hibernateCascadeAnnotation,
boolean orphanRemoval,
boolean forcePersist) {
EnumSet<CascadeType> hibernateCascadeSet = convertToHibernateCascadeType( ejbCascades );
CascadeType[] hibernateCascades = hibernateCascadeAnnotation == null ?
null :
hibernateCascadeAnnotation.value();
if ( hibernateCascades != null && hibernateCascades.length > 0 ) {
hibernateCascadeSet.addAll( Arrays.asList( hibernateCascades ) );
}
if ( orphanRemoval ) {
hibernateCascadeSet.add( CascadeType.DELETE_ORPHAN );
hibernateCascadeSet.add( CascadeType.REMOVE );
}
if ( forcePersist ) {
hibernateCascadeSet.add( CascadeType.PERSIST );
}
StringBuilder cascade = new StringBuilder();
for ( CascadeType aHibernateCascadeSet : hibernateCascadeSet ) {
switch ( aHibernateCascadeSet ) {
case ALL:
cascade.append( "," ).append( "all" );
break;
case SAVE_UPDATE:
cascade.append( "," ).append( "save-update" );
break;
case PERSIST:
cascade.append( "," ).append( "persist" );
break;
case MERGE:
cascade.append( "," ).append( "merge" );
break;
case LOCK:
cascade.append( "," ).append( "lock" );
break;
case REFRESH:
cascade.append( "," ).append( "refresh" );
break;
case REPLICATE:
cascade.append( "," ).append( "replicate" );
break;
case EVICT:
case DETACH:
cascade.append( "," ).append( "evict" );
break;
case DELETE:
cascade.append( "," ).append( "delete" );
break;
case DELETE_ORPHAN:
cascade.append( "," ).append( "delete-orphan" );
break;
case REMOVE:
cascade.append( "," ).append( "delete" );
break;
}
}
return cascade.length() > 0 ?
cascade.substring( 1 ) :
"none";
}
public static FetchMode getFetchMode(FetchType fetch) {
if ( fetch == FetchType.EAGER ) {
return FetchMode.JOIN;
}
else {
return FetchMode.SELECT;
}
}
private static HashMap<String, IdentifierGeneratorDefinition> buildGenerators(XAnnotatedElement annElt, MetadataBuildingContext context) {
InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
HashMap<String, IdentifierGeneratorDefinition> generators = new HashMap<>();
TableGenerators tableGenerators = annElt.getAnnotation( TableGenerators.class );
if ( tableGenerators != null ) {
for ( TableGenerator tableGenerator : tableGenerators.value() ) {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator(
tableGenerator,
context
);
generators.put(
idGenerator.getName(),
idGenerator
);
metadataCollector.addIdentifierGenerator( idGenerator );
}
}
SequenceGenerators sequenceGenerators = annElt.getAnnotation( SequenceGenerators.class );
if ( sequenceGenerators != null ) {
for ( SequenceGenerator sequenceGenerator : sequenceGenerators.value() ) {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator(
sequenceGenerator,
context
);
generators.put(
idGenerator.getName(),
idGenerator
);
metadataCollector.addIdentifierGenerator( idGenerator );
}
}
TableGenerator tabGen = annElt.getAnnotation( TableGenerator.class );
SequenceGenerator seqGen = annElt.getAnnotation( SequenceGenerator.class );
GenericGenerator genGen = annElt.getAnnotation( GenericGenerator.class );
if ( tabGen != null ) {
IdentifierGeneratorDefinition idGen = buildIdGenerator( tabGen, context );
generators.put( idGen.getName(), idGen );
metadataCollector.addIdentifierGenerator( idGen );
}
if ( seqGen != null ) {
IdentifierGeneratorDefinition idGen = buildIdGenerator( seqGen, context );
generators.put( idGen.getName(), idGen );
metadataCollector.addIdentifierGenerator( idGen );
}
if ( genGen != null ) {
IdentifierGeneratorDefinition idGen = buildIdGenerator( genGen, context );
generators.put( idGen.getName(), idGen );
metadataCollector.addIdentifierGenerator( idGen );
}
return generators;
}
public static boolean isDefault(XClass clazz, MetadataBuildingContext context) {
return context.getBootstrapContext().getReflectionManager().equals( clazz, void.class );
}
public static Map<XClass, InheritanceState> buildInheritanceStates(
List<XClass> orderedClasses,
MetadataBuildingContext buildingContext) {
Map<XClass, InheritanceState> inheritanceStatePerClass = new HashMap<>(
orderedClasses.size()
);
for ( XClass clazz : orderedClasses ) {
InheritanceState superclassState = InheritanceState.getSuperclassInheritanceState(
clazz, inheritanceStatePerClass
);
InheritanceState state = new InheritanceState( clazz, inheritanceStatePerClass, buildingContext );
if ( superclassState != null ) {
superclassState.setHasSiblings( true );
InheritanceState superEntityState = InheritanceState.getInheritanceStateOfSuperEntity(
clazz, inheritanceStatePerClass
);
state.setHasParents( superEntityState != null );
final boolean nonDefault = state.getType() != null && !InheritanceType.SINGLE_TABLE
.equals( state.getType() );
if ( superclassState.getType() != null ) {
final boolean mixingStrategy = state.getType() != null && !state.getType()
.equals( superclassState.getType() );
if ( nonDefault && mixingStrategy ) {
LOG.invalidSubStrategy( clazz.getName() );
}
state.setType( superclassState.getType() );
}
}
inheritanceStatePerClass.put( clazz, state );
}
return inheritanceStatePerClass;
}
private static boolean hasAnnotationsOnIdClass(XClass idClass) {
List<XProperty> properties = idClass.getDeclaredProperties( XClass.ACCESS_FIELD );
for ( XProperty property : properties ) {
if ( 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 )
) {
return true;
}
}
List<XMethod> methods = idClass.getDeclaredMethods();
for ( XMethod method : methods ) {
if ( method.isAnnotationPresent( Column.class ) || method.isAnnotationPresent( OneToMany.class ) ||
method.isAnnotationPresent( ManyToOne.class ) || method.isAnnotationPresent( Id.class ) ||
method.isAnnotationPresent( GeneratedValue.class ) || method.isAnnotationPresent( OneToOne.class ) ||
method.isAnnotationPresent( ManyToMany.class )
) {
return true;
}
}
return false;
}
}