use of org.hibernate.mapping.Property in project hibernate-orm by hibernate.
the class AnnotationBinder method processElementAnnotations.
/*
* Process annotation of a particular property
*/
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;
}
}
/**
* inSecondPass can only be used to apply right away the second pass of a composite-element
* Because it's a value type, there is no bidirectional association, hence second pass
* ordering does not matter
*/
final 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();
//prepare PropertyBinder
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);
//If version is on a mapped superclass, update the mapping
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(inferredData.getDeclaringClass(), inheritanceStatePerClass, context);
if (superclass != null) {
superclass.setDeclaredVersion(prop);
} else {
//we know the property is on the actual entity
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);
//check validity
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);
//check validity
if (property.isAnnotationPresent(Column.class) || property.isAnnotationPresent(Columns.class)) {
throw new AnnotationException("@Column(s) not allowed on a @OneToOne property: " + BinderHelper.getPath(propertyHolder, inferredData));
}
//FIXME support a proper PKJCs
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());
}
}
//MapsId means the columns belong to the pk => not null
//@OneToOne with @PKJC can still be optional
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)) {
//check validity
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), //@Any has not cascade attribute
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 {
//if @IndexColumn is not there, the generated IndexColumn is an implicit column and not used.
//so we can leave the legacy processing as the default
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;
//do not use "element" if you are a JPA 2 @ElementCollection only for legacy Hibernate mappings
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;
//JPA 2 has priority and has different default column values, differenciate legacy from JPA 2
Boolean isJPA2 = null;
if (property.isAnnotationPresent(MapKeyColumn.class)) {
isJPA2 = Boolean.TRUE;
keyColumns = new Column[] { new MapKeyColumnDelegator(property.getAnnotation(MapKeyColumn.class)) };
}
//not explicitly legacy
if (isJPA2 == null) {
isJPA2 = Boolean.TRUE;
}
//nullify empty array
keyColumns = keyColumns != null && keyColumns.length > 0 ? keyColumns : null;
//"mapkey" is the legacy column name of the key column pre JPA 2
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;
//JPA 2 has priority and has different default column values, differenciate legacy from JPA 2
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)) };
}
//not explicitly legacy
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);
}
//potential element
collectionBinder.setEmbedded(property.isAnnotationPresent(Embedded.class));
collectionBinder.setElementColumns(elementColumns);
collectionBinder.setProperty(property);
//TODO enhance exception with @ManyToAny and @CollectionOfElements
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.getBuildingOptions().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.getBuildingOptions().getReflectionManager().toXClass(targetElement));
//collectionBinder.setCascadeStrategy( getCascadeStrategy( embeddedCollectionAnn.cascade(), hibernateCascade ) );
collectionBinder.setOneToMany(true);
} else if (manyToManyAnn != null) {
mappedBy = manyToManyAnn.mappedBy();
collectionBinder.setTargetEntity(context.getBuildingOptions().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.getBuildingOptions().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)) {
//do not compute the generators unless necessary
HashMap<String, IdentifierGeneratorDefinition> localGenerators = (HashMap<String, IdentifierGeneratorDefinition>) classGenerators.clone();
localGenerators.putAll(buildLocalGenerators(property, context));
collectionBinder.setLocalGenerators(localGenerators);
}
collectionBinder.setInheritanceStatePerClass(inheritanceStatePerClass);
collectionBinder.setDeclaringClass(inferredData.getDeclaringClass());
collectionBinder.bind();
} else //Either a regular property or a basic @Id or @EmbeddedId while not ignoring id annotations
if (!isId || !entityBinder.isIgnoreIdAnnotations()) {
//define whether the type is a component or not
boolean isComponent = false;
//Overrides from @MapsId if needed
boolean isOverridden = false;
if (isId || propertyHolder.isOrWithinEmbeddedId() || propertyHolder.isInIdClass()) {
//the associated entity could be using an @IdClass making the overridden property a component
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();
}
//Get the new column
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 {
//provide the basic property mapping
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;
}
//implicit type will check basic types and Serializable classes
if (isId || (!optional && nullability != Nullability.FORCED_NULL)) {
//force columns to not 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);
Map<String, IdentifierGeneratorDefinition> localGenerators = (HashMap<String, IdentifierGeneratorDefinition>) classGenerators.clone();
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();
localGenerators.put(foreignGenerator.getName(), foreignGenerator);
BinderHelper.makeIdGenerator((SimpleValue) propertyBinder.getValue(), foreignGenerator.getStrategy(), foreignGenerator.getName(), context, localGenerators);
}
if (isId) {
//components and regular basic types create SimpleValue objects
final SimpleValue value = (SimpleValue) propertyBinder.getValue();
if (!isOverridden) {
processId(propertyHolder, inferredData, value, classGenerators, isIdentifierMapper, context);
}
}
}
}
//init index
//process indexes afterQuery everything: in second pass, many to one has to be done beforeQuery indexes
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);
}
}
}
}
// Natural ID columns must reside in one single UniqueKey within the Table.
// For now, simply ensure consistent naming.
// TODO: AFAIK, there really isn't a reason for these UKs to be created
// on the secondPass. This whole area should go away...
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);
}
}
}
}
use of org.hibernate.mapping.Property in project hibernate-orm by hibernate.
the class AnnotationBinder method bindIdClass.
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) {
/*
* Fill simple value and property since and Id is a property
*/
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();
HashMap<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
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");
}
//tuplizers
XProperty property = inferredData.getProperty();
setupComponentTuplizer(property, componentId);
} else {
for (Ejb3Column column : columns) {
//this is an id
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);
BinderHelper.makeIdGenerator(id, generatorType, generatorName, buildingContext, Collections.<String, IdentifierGeneratorDefinition>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);
//if the id property is on a superclass, update the metamodel
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(inferredData.getDeclaringClass(), inheritanceStatePerClass, buildingContext);
if (superclass != null) {
superclass.setDeclaredIdentifierProperty(prop);
} else {
//we know the property is on the actual entity
rootClass.setDeclaredIdentifierProperty(prop);
}
}
}
use of org.hibernate.mapping.Property in project hibernate-orm by hibernate.
the class ToOneFkSecondPass method isInPrimaryKey.
@Override
public boolean isInPrimaryKey() {
if (entityClassName == null)
return false;
final PersistentClass persistentClass = buildingContext.getMetadataCollector().getEntityBinding(entityClassName);
Property property = persistentClass.getIdentifierProperty();
if (path == null) {
return false;
} else if (property != null) {
//try explicit identifier property
return path.startsWith(property.getName() + ".");
} else {
//embedded property starts their path with 'id.' See PropertyPreloadedData( ) use when idClass != null in AnnotationSourceProcessor
if (path.startsWith("id.")) {
KeyValue valueIdentifier = persistentClass.getIdentifier();
String localPath = path.substring(3);
if (valueIdentifier instanceof Component) {
Iterator it = ((Component) valueIdentifier).getPropertyIterator();
while (it.hasNext()) {
Property idProperty = (Property) it.next();
if (localPath.startsWith(idProperty.getName()))
return true;
}
}
}
}
return false;
}
use of org.hibernate.mapping.Property in project hibernate-orm by hibernate.
the class CollectionBinder method bindManyToManySecondPass.
protected void bindManyToManySecondPass(Collection collValue, Map persistentClasses, Ejb3JoinColumn[] joinColumns, Ejb3JoinColumn[] inverseJoinColumns, Ejb3Column[] elementColumns, boolean isEmbedded, XClass collType, boolean ignoreNotFound, boolean unique, boolean cascadeDeleteEnabled, TableBinder associationTableBinder, XProperty property, PropertyHolder parentPropertyHolder, MetadataBuildingContext buildingContext) throws MappingException {
if (property == null) {
throw new IllegalArgumentException("null was passed for argument property");
}
final PersistentClass collectionEntity = (PersistentClass) persistentClasses.get(collType.getName());
final String hqlOrderBy = extractHqlOrderBy(jpaOrderBy);
boolean isCollectionOfEntities = collectionEntity != null;
ManyToAny anyAnn = property.getAnnotation(ManyToAny.class);
if (LOG.isDebugEnabled()) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
if (isCollectionOfEntities && unique) {
LOG.debugf("Binding a OneToMany: %s through an association table", path);
} else if (isCollectionOfEntities) {
LOG.debugf("Binding as ManyToMany: %s", path);
} else if (anyAnn != null) {
LOG.debugf("Binding a ManyToAny: %s", path);
} else {
LOG.debugf("Binding a collection of element: %s", path);
}
}
//check for user error
if (!isCollectionOfEntities) {
if (property.isAnnotationPresent(ManyToMany.class) || property.isAnnotationPresent(OneToMany.class)) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
throw new AnnotationException("Use of @OneToMany or @ManyToMany targeting an unmapped class: " + path + "[" + collType + "]");
} else if (anyAnn != null) {
if (parentPropertyHolder.getJoinTable(property) == null) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
throw new AnnotationException("@JoinTable is mandatory when @ManyToAny is used: " + path);
}
} else {
JoinTable joinTableAnn = parentPropertyHolder.getJoinTable(property);
if (joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
throw new AnnotationException("Use of @JoinTable.inverseJoinColumns targeting an unmapped class: " + path + "[" + collType + "]");
}
}
}
boolean mappedBy = !BinderHelper.isEmptyAnnotationValue(joinColumns[0].getMappedBy());
if (mappedBy) {
if (!isCollectionOfEntities) {
StringBuilder error = new StringBuilder(80).append("Collection of elements must not have mappedBy or association reference an unmapped entity: ").append(collValue.getOwnerEntityName()).append(".").append(joinColumns[0].getPropertyName());
throw new AnnotationException(error.toString());
}
Property otherSideProperty;
try {
otherSideProperty = collectionEntity.getRecursiveProperty(joinColumns[0].getMappedBy());
} catch (MappingException e) {
throw new AnnotationException("mappedBy reference an unknown target entity property: " + collType + "." + joinColumns[0].getMappedBy() + " in " + collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName());
}
Table table;
if (otherSideProperty.getValue() instanceof Collection) {
//this is a collection on the other side
table = ((Collection) otherSideProperty.getValue()).getCollectionTable();
} else {
//This is a ToOne with a @JoinTable or a regular property
table = otherSideProperty.getValue().getTable();
}
collValue.setCollectionTable(table);
String entityName = collectionEntity.getEntityName();
for (Ejb3JoinColumn column : joinColumns) {
//column.setDefaultColumnHeader( joinColumns[0].getMappedBy() ); //seems not to be used, make sense
column.setManyToManyOwnerSideEntityName(entityName);
}
} else {
//FIXME NamingStrategy
for (Ejb3JoinColumn column : joinColumns) {
String mappedByProperty = buildingContext.getMetadataCollector().getFromMappedBy(collValue.getOwnerEntityName(), column.getPropertyName());
Table ownerTable = collValue.getOwner().getTable();
column.setMappedBy(collValue.getOwner().getEntityName(), collValue.getOwner().getJpaEntityName(), buildingContext.getMetadataCollector().getLogicalTableName(ownerTable), mappedByProperty);
// String header = ( mappedByProperty == null ) ? mappings.getLogicalTableName( ownerTable ) : mappedByProperty;
// column.setDefaultColumnHeader( header );
}
if (StringHelper.isEmpty(associationTableBinder.getName())) {
//default value
associationTableBinder.setDefaultName(collValue.getOwner().getClassName(), collValue.getOwner().getEntityName(), collValue.getOwner().getJpaEntityName(), buildingContext.getMetadataCollector().getLogicalTableName(collValue.getOwner().getTable()), collectionEntity != null ? collectionEntity.getClassName() : null, collectionEntity != null ? collectionEntity.getEntityName() : null, collectionEntity != null ? collectionEntity.getJpaEntityName() : null, collectionEntity != null ? buildingContext.getMetadataCollector().getLogicalTableName(collectionEntity.getTable()) : null, joinColumns[0].getPropertyName());
}
associationTableBinder.setJPA2ElementCollection(!isCollectionOfEntities && property.isAnnotationPresent(ElementCollection.class));
collValue.setCollectionTable(associationTableBinder.bind());
}
bindFilters(isCollectionOfEntities);
bindCollectionSecondPass(collValue, collectionEntity, joinColumns, cascadeDeleteEnabled, property, propertyHolder, buildingContext);
ManyToOne element = null;
if (isCollectionOfEntities) {
element = new ManyToOne(buildingContext.getMetadataCollector(), collValue.getCollectionTable());
collValue.setElement(element);
element.setReferencedEntityName(collType.getName());
//element.setFetchMode( fetchMode );
//element.setLazy( fetchMode != FetchMode.JOIN );
//make the second join non lazy
element.setFetchMode(FetchMode.JOIN);
element.setLazy(false);
element.setIgnoreNotFound(ignoreNotFound);
// as per 11.1.38 of JPA 2.0 spec, default to primary key if no column is specified by @OrderBy.
if (hqlOrderBy != null) {
collValue.setManyToManyOrdering(buildOrderByClauseFromHql(hqlOrderBy, collectionEntity, collValue.getRole()));
}
final ForeignKey fk = property.getAnnotation(ForeignKey.class);
if (fk != null && !BinderHelper.isEmptyAnnotationValue(fk.name())) {
element.setForeignKeyName(fk.name());
} else {
final JoinTable joinTableAnn = property.getAnnotation(JoinTable.class);
if (joinTableAnn != null) {
String foreignKeyName = joinTableAnn.inverseForeignKey().name();
String foreignKeyDefinition = joinTableAnn.inverseForeignKey().foreignKeyDefinition();
ConstraintMode foreignKeyValue = joinTableAnn.inverseForeignKey().value();
if (joinTableAnn.inverseJoinColumns().length != 0) {
final JoinColumn joinColumnAnn = joinTableAnn.inverseJoinColumns()[0];
if ("".equals(foreignKeyName)) {
foreignKeyName = joinColumnAnn.foreignKey().name();
foreignKeyDefinition = joinColumnAnn.foreignKey().foreignKeyDefinition();
}
if (foreignKeyValue != ConstraintMode.NO_CONSTRAINT) {
foreignKeyValue = joinColumnAnn.foreignKey().value();
}
}
if (joinTableAnn.inverseForeignKey().value() == ConstraintMode.NO_CONSTRAINT) {
element.setForeignKeyName("none");
} else {
element.setForeignKeyName(StringHelper.nullIfEmpty(foreignKeyName));
element.setForeignKeyDefinition(StringHelper.nullIfEmpty(foreignKeyDefinition));
}
}
}
} else if (anyAnn != null) {
//@ManyToAny
//Make sure that collTyp is never used during the @ManyToAny branch: it will be set to void.class
PropertyData inferredData = new PropertyInferredData(null, property, "unsupported", buildingContext.getBuildingOptions().getReflectionManager());
//override the table
for (Ejb3Column column : inverseJoinColumns) {
column.setTable(collValue.getCollectionTable());
}
Any any = BinderHelper.buildAnyValue(anyAnn.metaDef(), inverseJoinColumns, anyAnn.metaColumn(), inferredData, cascadeDeleteEnabled, Nullability.NO_CONSTRAINT, propertyHolder, new EntityBinder(), true, buildingContext);
collValue.setElement(any);
} else {
XClass elementClass;
AnnotatedClassType classType;
CollectionPropertyHolder holder = null;
if (BinderHelper.PRIMITIVE_NAMES.contains(collType.getName())) {
classType = AnnotatedClassType.NONE;
elementClass = null;
holder = PropertyHolderBuilder.buildPropertyHolder(collValue, collValue.getRole(), null, property, parentPropertyHolder, buildingContext);
} else {
elementClass = collType;
classType = buildingContext.getMetadataCollector().getClassType(elementClass);
holder = PropertyHolderBuilder.buildPropertyHolder(collValue, collValue.getRole(), elementClass, property, parentPropertyHolder, buildingContext);
// 'parentPropertyHolder' is the PropertyHolder for the owner of the collection
// 'holder' is the CollectionPropertyHolder.
// 'property' is the collection XProperty
parentPropertyHolder.startingProperty(property);
//force in case of attribute override
boolean attributeOverride = property.isAnnotationPresent(AttributeOverride.class) || property.isAnnotationPresent(AttributeOverrides.class);
// todo : force in the case of Convert annotation(s) with embedded paths (beyond key/value prefixes)?
if (isEmbedded || attributeOverride) {
classType = AnnotatedClassType.EMBEDDABLE;
}
}
if (AnnotatedClassType.EMBEDDABLE.equals(classType)) {
holder.prepare(property);
EntityBinder entityBinder = new EntityBinder();
PersistentClass owner = collValue.getOwner();
boolean isPropertyAnnotated;
//String accessType = access != null ? access.value() : null;
if (owner.getIdentifierProperty() != null) {
isPropertyAnnotated = owner.getIdentifierProperty().getPropertyAccessorName().equals("property");
} else if (owner.getIdentifierMapper() != null && owner.getIdentifierMapper().getPropertySpan() > 0) {
Property prop = (Property) owner.getIdentifierMapper().getPropertyIterator().next();
isPropertyAnnotated = prop.getPropertyAccessorName().equals("property");
} else {
throw new AssertionFailure("Unable to guess collection property accessor name");
}
PropertyData inferredData;
if (isMap()) {
//"value" is the JPA 2 prefix for map values (used to be "element")
if (isHibernateExtensionMapping()) {
inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "element", elementClass);
} else {
inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "value", elementClass);
}
} else {
if (isHibernateExtensionMapping()) {
inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "element", elementClass);
} else {
//"collection&&element" is not a valid property name => placeholder
inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "collection&&element", elementClass);
}
}
//TODO be smart with isNullable
boolean isNullable = true;
Component component = AnnotationBinder.fillComponent(holder, inferredData, isPropertyAnnotated ? AccessType.PROPERTY : AccessType.FIELD, isNullable, entityBinder, false, false, true, buildingContext, inheritanceStatePerClass);
collValue.setElement(component);
if (StringHelper.isNotEmpty(hqlOrderBy)) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment(hqlOrderBy);
if (orderBy != null) {
collValue.setOrderBy(orderBy);
}
}
} else {
holder.prepare(property);
SimpleValueBinder elementBinder = new SimpleValueBinder();
elementBinder.setBuildingContext(buildingContext);
elementBinder.setReturnedClassName(collType.getName());
if (elementColumns == null || elementColumns.length == 0) {
elementColumns = new Ejb3Column[1];
Ejb3Column column = new Ejb3Column();
column.setImplicit(false);
//not following the spec but more clean
column.setNullable(true);
column.setLength(Ejb3Column.DEFAULT_COLUMN_LENGTH);
column.setLogicalColumnName(Collection.DEFAULT_ELEMENT_COLUMN_NAME);
//TODO create an EMPTY_JOINS collection
column.setJoins(new HashMap<String, Join>());
column.setBuildingContext(buildingContext);
column.bind();
elementColumns[0] = column;
}
//override the table
for (Ejb3Column column : elementColumns) {
column.setTable(collValue.getCollectionTable());
}
elementBinder.setColumns(elementColumns);
elementBinder.setType(property, elementClass, collValue.getOwnerEntityName(), holder.resolveElementAttributeConverterDescriptor(property, elementClass));
elementBinder.setPersistentClassName(propertyHolder.getEntityName());
elementBinder.setAccessType(accessType);
collValue.setElement(elementBinder.make());
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment(hqlOrderBy);
if (orderBy != null) {
collValue.setOrderBy(orderBy);
}
}
}
checkFilterConditions(collValue);
//FIXME: do optional = false
if (isCollectionOfEntities) {
bindManytoManyInverseFk(collectionEntity, inverseJoinColumns, element, unique, buildingContext);
}
}
use of org.hibernate.mapping.Property in project hibernate-orm by hibernate.
the class CollectionBinder method bindManytoManyInverseFk.
/**
* bind the inverse FK of a ManyToMany
* If we are in a mappedBy case, read the columns from the associated
* collection element
* Otherwise delegates to the usual algorithm
*/
public static void bindManytoManyInverseFk(PersistentClass referencedEntity, Ejb3JoinColumn[] columns, SimpleValue value, boolean unique, MetadataBuildingContext buildingContext) {
final String mappedBy = columns[0].getMappedBy();
if (StringHelper.isNotEmpty(mappedBy)) {
final Property property = referencedEntity.getRecursiveProperty(mappedBy);
Iterator mappedByColumns;
if (property.getValue() instanceof Collection) {
mappedByColumns = ((Collection) property.getValue()).getKey().getColumnIterator();
} else {
//find the appropriate reference key, can be in a join
Iterator joinsIt = referencedEntity.getJoinIterator();
KeyValue key = null;
while (joinsIt.hasNext()) {
Join join = (Join) joinsIt.next();
if (join.containsProperty(property)) {
key = join.getKey();
break;
}
}
if (key == null)
key = property.getPersistentClass().getIdentifier();
mappedByColumns = key.getColumnIterator();
}
while (mappedByColumns.hasNext()) {
Column column = (Column) mappedByColumns.next();
columns[0].linkValueUsingAColumnCopy(column, value);
}
String referencedPropertyName = buildingContext.getMetadataCollector().getPropertyReferencedAssociation("inverse__" + referencedEntity.getEntityName(), mappedBy);
if (referencedPropertyName != null) {
//TODO always a many to one?
((ManyToOne) value).setReferencedPropertyName(referencedPropertyName);
buildingContext.getMetadataCollector().addUniquePropertyReference(referencedEntity.getEntityName(), referencedPropertyName);
}
((ManyToOne) value).setReferenceToPrimaryKey(referencedPropertyName == null);
value.createForeignKey();
} else {
BinderHelper.createSyntheticPropertyReference(columns, referencedEntity, null, value, true, buildingContext);
TableBinder.bindFk(referencedEntity, null, columns, value, unique, buildingContext);
}
}
Aggregations