use of jakarta.persistence.JoinColumns in project hibernate-orm by hibernate.
the class AnnotationBinder method bindCollection.
private static void bindCollection(PropertyHolder propertyHolder, Nullability nullability, PropertyData inferredData, Map<String, IdentifierGeneratorDefinition> classGenerators, EntityBinder entityBinder, boolean isIdentifierMapper, MetadataBuildingContext context, Map<XClass, InheritanceState> inheritanceStatePerClass, XProperty property, AnnotatedJoinColumn[] joinColumns) {
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));
}
if (property.isAnnotationPresent(OrderColumn.class) && manyToManyAnn != null && !manyToManyAnn.mappedBy().isEmpty()) {
throw new AnnotationException("Explicit @OrderColumn on inverse side of @ManyToMany is illegal: " + BinderHelper.getPath(propertyHolder, inferredData));
}
final IndexColumn indexColumn = IndexColumn.fromAnnotations(property.getAnnotation(OrderColumn.class), property.getAnnotation(org.hibernate.annotations.IndexColumn.class), property.getAnnotation(ListIndexBase.class), propertyHolder, inferredData, entityBinder.getSecondaryTables(), context);
CollectionBinder collectionBinder = getCollectionBinder(property, hasMapKeyAnnotation(property), context);
collectionBinder.setIndexColumn(indexColumn);
collectionBinder.setMapKey(property.getAnnotation(MapKey.class));
collectionBinder.setPropertyName(inferredData.getPropertyName());
collectionBinder.setBatchSize(property.getAnnotation(BatchSize.class));
collectionBinder.setJpaOrderBy(property.getAnnotation(jakarta.persistence.OrderBy.class));
collectionBinder.setSqlOrderBy(getOverridableAnnotation(property, OrderBy.class, context));
collectionBinder.setNaturalSort(property.getAnnotation(SortNatural.class));
collectionBinder.setComparatorSort(property.getAnnotation(SortComparator.class));
collectionBinder.setCache(property.getAnnotation(Cache.class));
collectionBinder.setPropertyHolder(propertyHolder);
Cascade hibernateCascade = property.getAnnotation(Cascade.class);
NotFound notFound = property.getAnnotation(NotFound.class);
collectionBinder.setIgnoreNotFound(notFound != null && notFound.action() == NotFoundAction.IGNORE);
collectionBinder.setCollectionType(inferredData.getProperty().getElementClass());
collectionBinder.setAccessType(inferredData.getDefaultAccess());
AnnotatedColumn[] elementColumns;
// do not use "element" if you are a JPA 2 @ElementCollection, only for legacy Hibernate mappings
PropertyData virtualProperty = property.isAnnotationPresent(ElementCollection.class) ? inferredData : new WrappedInferredData(inferredData, "element");
Comment comment = property.getAnnotation(Comment.class);
if (property.isAnnotationPresent(Column.class)) {
elementColumns = buildColumnFromAnnotation(property.getAnnotation(Column.class), comment, nullability, propertyHolder, virtualProperty, entityBinder.getSecondaryTables(), context);
} else if (property.isAnnotationPresent(Formula.class)) {
elementColumns = buildFormulaFromAnnotation(getOverridableAnnotation(property, Formula.class, context), comment, nullability, propertyHolder, virtualProperty, entityBinder.getSecondaryTables(), context);
} else if (property.isAnnotationPresent(Columns.class)) {
elementColumns = buildColumnsFromAnnotations(property.getAnnotation(Columns.class).columns(), comment, nullability, propertyHolder, virtualProperty, entityBinder.getSecondaryTables(), context);
} else {
elementColumns = buildColumnFromNoAnnotation(comment, nullability, propertyHolder, virtualProperty, entityBinder.getSecondaryTables(), context);
}
JoinColumn[] joinKeyColumns = mapKeyColumns(propertyHolder, inferredData, entityBinder, context, property, collectionBinder, comment);
AnnotatedJoinColumn[] mapJoinColumns = buildJoinColumnsWithDefaultColumnSuffix(joinKeyColumns, comment, null, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), "_KEY", 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;
ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();
if (oneToManyAnn != null) {
for (AnnotatedJoinColumn column : joinColumns) {
if (column.isSecondary()) {
throw new NotYetImplementedException("Collections having FK in secondary table");
}
}
collectionBinder.setFkJoinColumns(joinColumns);
mappedBy = oneToManyAnn.mappedBy();
// noinspection unchecked
collectionBinder.setTargetEntity(reflectionManager.toXClass(oneToManyAnn.targetEntity()));
collectionBinder.setCascadeStrategy(getCascadeStrategy(oneToManyAnn.cascade(), hibernateCascade, oneToManyAnn.orphanRemoval(), false));
collectionBinder.setOneToMany(true);
} else if (elementCollectionAnn != null) {
for (AnnotatedJoinColumn 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(reflectionManager.toXClass(targetElement));
// collectionBinder.setCascadeStrategy( getCascadeStrategy( embeddedCollectionAnn.cascade(), hibernateCascade ) );
collectionBinder.setOneToMany(true);
} else if (manyToManyAnn != null) {
mappedBy = manyToManyAnn.mappedBy();
// noinspection unchecked
collectionBinder.setTargetEntity(reflectionManager.toXClass(manyToManyAnn.targetEntity()));
collectionBinder.setCascadeStrategy(getCascadeStrategy(manyToManyAnn.cascade(), hibernateCascade, false, false));
collectionBinder.setOneToMany(false);
} else if (property.isAnnotationPresent(ManyToAny.class)) {
mappedBy = "";
collectionBinder.setTargetEntity(reflectionManager.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 == 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 = new HashMap<>(classGenerators);
localGenerators.putAll(buildGenerators(property, context));
collectionBinder.setLocalGenerators(localGenerators);
}
collectionBinder.setInheritanceStatePerClass(inheritanceStatePerClass);
collectionBinder.setDeclaringClass(inferredData.getDeclaringClass());
collectionBinder.bind();
}
use of jakarta.persistence.JoinColumns in project hibernate-orm by hibernate.
the class AnnotationBinder method bindManyToOne.
private static void bindManyToOne(PropertyHolder propertyHolder, PropertyData inferredData, boolean isIdentifierMapper, boolean inSecondPass, MetadataBuildingContext context, XProperty property, AnnotatedJoinColumn[] joinColumns, PropertyBinder propertyBinder, boolean forcePersist) {
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() == NotFoundAction.IGNORE;
matchIgnoreNotFoundWithFetchType(propertyHolder.getEntityName(), property.getName(), ignoreNotFound, ann.fetch());
OnDelete onDeleteAnn = property.getAnnotation(OnDelete.class);
JoinTable assocTable = propertyHolder.getJoinTable(property);
if (assocTable != null) {
Join join = propertyHolder.addJoin(assocTable, false);
for (AnnotatedJoinColumn joinColumn : joinColumns) {
joinColumn.setExplicitTableName(join.getTable().getName());
}
}
// MapsId means the columns belong to the pk;
// A @MapsId association (obviously) must be non-null when the entity is first persisted.
// If a @MapsId association is not mapped with @NotFound(IGNORE), then the association
// is mandatory (even if the association has optional=true).
// If a @MapsId association has optional=true and is mapped with @NotFound(IGNORE) then
// the association is optional.
final boolean mandatory = !ann.optional() || property.isAnnotationPresent(Id.class) || property.isAnnotationPresent(MapsId.class) && !ignoreNotFound;
bindManyToOne(getCascadeStrategy(ann.cascade(), hibernateCascade, false, forcePersist), joinColumns, !mandatory, ignoreNotFound, onDeleteAnn != null && OnDeleteAction.CASCADE == onDeleteAnn.action(), ToOneBinder.getTargetEntity(inferredData, context), propertyHolder, inferredData, false, isIdentifierMapper, inSecondPass, propertyBinder, context);
}
use of jakarta.persistence.JoinColumns in project hibernate-orm by hibernate.
the class ColumnsBuilder method buildExplicitJoinColumns.
AnnotatedJoinColumn[] buildExplicitJoinColumns(XProperty property, PropertyData inferredData) {
// process @JoinColumn(s) before @Column(s) to handle collection of entities properly
JoinColumn[] joinColumnAnnotations = null;
if (property.isAnnotationPresent(JoinColumn.class)) {
joinColumnAnnotations = new JoinColumn[] { property.getAnnotation(JoinColumn.class) };
} else if (property.isAnnotationPresent(JoinColumns.class)) {
JoinColumns joinColumnAnnotation = property.getAnnotation(JoinColumns.class);
joinColumnAnnotations = joinColumnAnnotation.value();
int length = joinColumnAnnotations.length;
if (length == 0) {
throw new AnnotationException("Cannot bind an empty @JoinColumns");
}
}
if (joinColumnAnnotations != null) {
return AnnotatedJoinColumn.buildJoinColumns(joinColumnAnnotations, property.getAnnotation(Comment.class), null, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), buildingContext);
}
JoinColumnOrFormula[] joinColumnOrFormulaAnnotations = null;
if (property.isAnnotationPresent(JoinColumnOrFormula.class)) {
joinColumnOrFormulaAnnotations = new JoinColumnOrFormula[] { property.getAnnotation(JoinColumnOrFormula.class) };
} else if (property.isAnnotationPresent(JoinColumnsOrFormulas.class)) {
JoinColumnsOrFormulas joinColumnsOrFormulasAnnotations = property.getAnnotation(JoinColumnsOrFormulas.class);
joinColumnOrFormulaAnnotations = joinColumnsOrFormulasAnnotations.value();
int length = joinColumnOrFormulaAnnotations.length;
if (length == 0) {
throw new AnnotationException("Cannot bind an empty @JoinColumnsOrFormulas");
}
}
if (joinColumnOrFormulaAnnotations != null) {
return AnnotatedJoinColumn.buildJoinColumnsOrFormulas(joinColumnOrFormulaAnnotations, null, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), buildingContext);
}
if (property.isAnnotationPresent(JoinFormula.class)) {
JoinFormula ann = getOverridableAnnotation(property, JoinFormula.class, buildingContext);
AnnotatedJoinColumn[] annotatedJoinColumns = new AnnotatedJoinColumn[1];
annotatedJoinColumns[0] = AnnotatedJoinColumn.buildJoinFormula(ann, null, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), buildingContext);
return annotatedJoinColumns;
}
return null;
}
use of jakarta.persistence.JoinColumns in project hibernate-orm by hibernate.
the class JPAXMLOverriddenAnnotationReader method overridesDefaultsInJoinTable.
private JoinTable overridesDefaultsInJoinTable(Annotation annotation, XMLContext.Default defaults) {
// no element but might have some default or some annotation
boolean defaultToJoinTable = !(isPhysicalAnnotationPresent(JoinColumn.class) || isPhysicalAnnotationPresent(JoinColumns.class));
final Class<? extends Annotation> annotationClass = annotation.annotationType();
defaultToJoinTable = defaultToJoinTable && ((annotationClass == ManyToMany.class && StringHelper.isEmpty(((ManyToMany) annotation).mappedBy())) || (annotationClass == OneToMany.class && StringHelper.isEmpty(((OneToMany) annotation).mappedBy())) || (annotationClass == ElementCollection.class));
final Class<JoinTable> annotationType = JoinTable.class;
if (defaultToJoinTable && (StringHelper.isNotEmpty(defaults.getCatalog()) || StringHelper.isNotEmpty(defaults.getSchema()))) {
AnnotationDescriptor ad = new AnnotationDescriptor(annotationType);
if (defaults.canUseJavaAnnotations()) {
JoinTable table = getPhysicalAnnotation(annotationType);
if (table != null) {
ad.setValue("name", table.name());
ad.setValue("schema", table.schema());
ad.setValue("catalog", table.catalog());
ad.setValue("uniqueConstraints", table.uniqueConstraints());
ad.setValue("joinColumns", table.joinColumns());
ad.setValue("inverseJoinColumns", table.inverseJoinColumns());
}
}
if (StringHelper.isEmpty((String) ad.valueOf("schema")) && StringHelper.isNotEmpty(defaults.getSchema())) {
ad.setValue("schema", defaults.getSchema());
}
if (StringHelper.isEmpty((String) ad.valueOf("catalog")) && StringHelper.isNotEmpty(defaults.getCatalog())) {
ad.setValue("catalog", defaults.getCatalog());
}
return AnnotationFactory.create(ad);
} else if (defaults.canUseJavaAnnotations()) {
return getPhysicalAnnotation(annotationType);
} else {
return null;
}
}
use of jakarta.persistence.JoinColumns in project hibernate-orm by hibernate.
the class OneToOneSecondPass method doSecondPass.
// TODO refactor this code, there is a lot of duplication in this method
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
OneToOne value = new OneToOne(buildingContext, propertyHolder.getTable(), propertyHolder.getPersistentClass());
final String propertyName = inferredData.getPropertyName();
value.setPropertyName(propertyName);
String referencedEntityName = ToOneBinder.getReferenceEntityName(inferredData, targetEntity, buildingContext);
value.setReferencedEntityName(referencedEntityName);
AnnotationBinder.defineFetchingStrategy(value, inferredData.getProperty());
// value.setFetchMode( fetchMode );
value.setCascadeDeleteEnabled(cascadeOnDelete);
// value.setLazy( fetchMode != FetchMode.JOIN );
value.setConstrained(!optional);
final ForeignKeyDirection foreignKeyDirection = !BinderHelper.isEmptyAnnotationValue(mappedBy) ? ForeignKeyDirection.TO_PARENT : ForeignKeyDirection.FROM_PARENT;
value.setForeignKeyType(foreignKeyDirection);
AnnotationBinder.bindForeignKeyNameAndDefinition(value, inferredData.getProperty(), inferredData.getProperty().getAnnotation(jakarta.persistence.ForeignKey.class), inferredData.getProperty().getAnnotation(JoinColumn.class), inferredData.getProperty().getAnnotation(JoinColumns.class), buildingContext);
PropertyBinder binder = new PropertyBinder();
binder.setName(propertyName);
binder.setValue(value);
binder.setCascade(cascadeStrategy);
binder.setAccessType(inferredData.getDefaultAccess());
final LazyGroup lazyGroupAnnotation = inferredData.getProperty().getAnnotation(LazyGroup.class);
if (lazyGroupAnnotation != null) {
binder.setLazyGroup(lazyGroupAnnotation.value());
}
Property prop = binder.makeProperty();
prop.setOptional(optional);
if (BinderHelper.isEmptyAnnotationValue(mappedBy)) {
/*
* we need to check if the columns are in the right order
* if not, then we need to create a many to one and formula
* but actually, since entities linked by a one to one need
* to share the same composite id class, this cannot happen in hibernate
*/
boolean rightOrder = true;
if (rightOrder) {
String path = StringHelper.qualify(propertyHolder.getPath(), propertyName);
final ToOneFkSecondPass secondPass = new ToOneFkSecondPass(value, joinColumns, // cannot have nullabe and unique on certain DBs
!optional, propertyHolder.getEntityOwnerClassName(), path, buildingContext);
secondPass.doSecondPass(persistentClasses);
// no column associated since its a one to one
propertyHolder.addProperty(prop, inferredData.getDeclaringClass());
}
// else {
// this is a many to one with Formula
// }
} else {
value.setMappedByProperty(mappedBy);
PersistentClass otherSide = persistentClasses.get(value.getReferencedEntityName());
Property otherSideProperty;
try {
if (otherSide == null) {
throw new MappingException("Unable to find entity: " + value.getReferencedEntityName());
}
otherSideProperty = BinderHelper.findPropertyByName(otherSide, mappedBy);
} catch (MappingException e) {
throw new AnnotationException("Unknown mappedBy in: " + StringHelper.qualify(ownerEntity, ownerProperty) + ", referenced property unknown: " + StringHelper.qualify(value.getReferencedEntityName(), mappedBy));
}
if (otherSideProperty == null) {
throw new AnnotationException("Unknown mappedBy in: " + StringHelper.qualify(ownerEntity, ownerProperty) + ", referenced property unknown: " + StringHelper.qualify(value.getReferencedEntityName(), mappedBy));
}
if (otherSideProperty.getValue() instanceof OneToOne) {
propertyHolder.addProperty(prop, inferredData.getDeclaringClass());
} else if (otherSideProperty.getValue() instanceof ManyToOne) {
Join otherSideJoin = null;
for (Join otherSideJoinValue : otherSide.getJoins()) {
if (otherSideJoinValue.containsProperty(otherSideProperty)) {
otherSideJoin = otherSideJoinValue;
break;
}
}
if (otherSideJoin != null) {
// @OneToOne @JoinTable
Join mappedByJoin = buildJoinFromMappedBySide(persistentClasses.get(ownerEntity), otherSideProperty, otherSideJoin);
ManyToOne manyToOne = new ManyToOne(buildingContext, mappedByJoin.getTable());
// FIXME use ignore not found here
manyToOne.setIgnoreNotFound(ignoreNotFound);
manyToOne.setCascadeDeleteEnabled(value.isCascadeDeleteEnabled());
manyToOne.setFetchMode(value.getFetchMode());
manyToOne.setLazy(value.isLazy());
manyToOne.setReferencedEntityName(value.getReferencedEntityName());
manyToOne.setUnwrapProxy(value.isUnwrapProxy());
manyToOne.markAsLogicalOneToOne();
prop.setValue(manyToOne);
for (Column column : otherSideJoin.getKey().getColumns()) {
Column copy = new Column();
copy.setLength(column.getLength());
copy.setScale(column.getScale());
copy.setValue(manyToOne);
copy.setName(column.getQuotedName());
copy.setNullable(column.isNullable());
copy.setPrecision(column.getPrecision());
copy.setUnique(column.isUnique());
copy.setSqlType(column.getSqlType());
copy.setCheckConstraint(column.getCheckConstraint());
copy.setComment(column.getComment());
copy.setDefaultValue(column.getDefaultValue());
copy.setGeneratedAs(column.getGeneratedAs());
manyToOne.addColumn(copy);
}
mappedByJoin.addProperty(prop);
} else {
propertyHolder.addProperty(prop, inferredData.getDeclaringClass());
}
value.setReferencedPropertyName(mappedBy);
// HHH-6813
// Foo: @Id long id, @OneToOne(mappedBy="foo") Bar bar
// Bar: @Id @OneToOne Foo foo
boolean referencesDerivedId = false;
try {
referencesDerivedId = otherSide.getIdentifier() instanceof Component && ((Component) otherSide.getIdentifier()).getProperty(mappedBy) != null;
} catch (MappingException e) {
// ignore
}
boolean referenceToPrimaryKey = referencesDerivedId || mappedBy == null;
value.setReferenceToPrimaryKey(referenceToPrimaryKey);
String propertyRef = value.getReferencedPropertyName();
if (propertyRef != null) {
buildingContext.getMetadataCollector().addUniquePropertyReference(value.getReferencedEntityName(), propertyRef);
}
} else {
throw new AnnotationException("Referenced property not a (One|Many)ToOne: " + StringHelper.qualify(otherSide.getEntityName(), mappedBy) + " in mappedBy of " + StringHelper.qualify(ownerEntity, ownerProperty));
}
}
value.sortProperties();
}
Aggregations