diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumn.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumn.java index d75bfd378ae3..34f8ac2bf553 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumn.java @@ -433,6 +433,26 @@ static AnnotatedJoinColumn buildExplicitJoinTableJoinColumn( return column; } + static AnnotatedJoinColumn buildExplicitJoinTableJoinFormula( + AnnotatedJoinColumns parent, + PropertyHolder propertyHolder, + PropertyData inferredData, + JoinFormula joinFormula) { + final AnnotatedJoinColumn column = new AnnotatedJoinColumn(); + column.setImplicit( true ); +// column.setPropertyHolder( propertyHolder ); +// column.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); +// column.setJoins( secondaryTables ); +// column.setContext( context ); + column.setNullable( false ); //I break the spec, but it's for good + //done after the annotation to override it + column.setParent( parent ); + column.setFormula( joinFormula.value() ); + column.setReferencedColumn( joinFormula.referencedColumnName() ); + column.bind(); + return column; + } + @Override public String toString() { final StringBuilder string = new StringBuilder(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumns.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumns.java index e82614d69a6a..3e6e254d8ad5 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumns.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumns.java @@ -209,6 +209,26 @@ public static AnnotatedJoinColumns buildJoinTableJoinColumns( return parent; } + /** + * Called for join tables in {@link jakarta.persistence.ManyToMany} associations. + */ + public static AnnotatedJoinColumns buildJoinTableJoinFormula( + JoinFormula joinFormula, + Map secondaryTables, + PropertyHolder propertyHolder, + PropertyData inferredData, + String mappedBy, + MetadataBuildingContext context) { + final AnnotatedJoinColumns parent = new AnnotatedJoinColumns(); + parent.setBuildingContext( context ); + parent.setJoins( secondaryTables ); + parent.setPropertyHolder( propertyHolder ); + parent.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) ); + parent.setMappedBy( mappedBy ); + AnnotatedJoinColumn.buildExplicitJoinTableJoinFormula( parent, propertyHolder, inferredData, joinFormula ); + return parent; + } + public List getJoinColumns() { return columns; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java index 024e34e4ff4b..4821af5522d6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java @@ -40,6 +40,7 @@ import org.hibernate.annotations.Formula; import org.hibernate.annotations.HQLSelect; import org.hibernate.annotations.Immutable; +import org.hibernate.annotations.JoinFormula; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; import org.hibernate.annotations.LazyGroup; @@ -149,6 +150,7 @@ import static org.hibernate.boot.model.internal.AnnotatedColumn.buildFormulaFromAnnotation; import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinColumnsWithDefaultColumnSuffix; import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinTableJoinColumns; +import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinTableJoinFormula; import static org.hibernate.boot.model.internal.BinderHelper.buildAnyValue; import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; import static org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference; @@ -641,6 +643,7 @@ private static void bindJoinedTableAssociation( final CollectionTable collectionTable = property.getAnnotation( CollectionTable.class ); final JoinColumn[] annJoins; final JoinColumn[] annInverseJoins; + final JoinFormula annJoinFormula; if ( assocTable != null || collectionTable != null ) { final String catalog; @@ -689,10 +692,25 @@ private static void bindJoinedTableAssociation( //set check constraint in the second pass annJoins = joins.length == 0 ? null : joins; annInverseJoins = inverseJoins == null || inverseJoins.length == 0 ? null : inverseJoins; + annJoinFormula = null; } else { + if ( property.isAnnotationPresent(JoinFormula.class) ) { + annJoinFormula = property.getAnnotation(JoinFormula.class); + } + else { + annJoinFormula = null; + } + if ( property.isAnnotationPresent(JoinColumns.class) ) { + annInverseJoins = property.getAnnotation(JoinColumns.class).value(); + } + else if ( property.isAnnotationPresent(JoinColumn.class) ) { + annInverseJoins = new JoinColumn[] { property.getAnnotation(JoinColumn.class) }; + } + else { + annInverseJoins = null; + } annJoins = null; - annInverseJoins = null; } associationTableBinder.setBuildingContext( buildingContext ); collectionBinder.setTableBinder( associationTableBinder ); @@ -704,14 +722,26 @@ private static void bindJoinedTableAssociation( mappedBy, buildingContext ) ); - collectionBinder.setInverseJoinColumns( buildJoinTableJoinColumns( - annInverseJoins, - entityBinder.getSecondaryTables(), - propertyHolder, - inferredData, - mappedBy, - buildingContext - ) ); + if ( annJoinFormula != null ) { + collectionBinder.setInverseJoinColumns( buildJoinTableJoinFormula( + annJoinFormula, + entityBinder.getSecondaryTables(), + propertyHolder, + inferredData, + mappedBy, + buildingContext + ) ); + } + else { + collectionBinder.setInverseJoinColumns( buildJoinTableJoinColumns( + annInverseJoins, + entityBinder.getSecondaryTables(), + propertyHolder, + inferredData, + mappedBy, + buildingContext + ) ); + } } protected MetadataBuildingContext getBuildingContext() { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinColumnOrFormula2Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinColumnOrFormula2Test.java new file mode 100644 index 000000000000..3c8c59670f4e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/formula/JoinColumnOrFormula2Test.java @@ -0,0 +1,68 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.annotations.formula; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import org.hibernate.annotations.JoinFormula; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SessionFactory +@DomainModel(annotatedClasses = {JoinColumnOrFormula2Test.A.class, JoinColumnOrFormula2Test.D.class}) +public class JoinColumnOrFormula2Test { + @Test + public void test(SessionFactoryScope scope) { + scope.inTransaction(s -> { + A a = new A(); + a.id = 3; + a.dId = 5; + D d = new D(); + d.id = 5; + s.persist(a); + s.persist(d); + a.ds.add(d); + }); + scope.inSession(s -> { + Set ds = s.get(A.class, 3).ds; + assertEquals(1, ds.size()); + }); + } + + @Entity( name = "A" ) + @Table( name = "A" ) + public static class A { + @Id + @Column( name = "aid") + public Integer id; + + @Column( name = "did") + public Integer dId; + + @ManyToMany + @JoinFormula(value = "A_aid+2", referencedColumnName = "did") + Set ds = new HashSet<>(); + } + + @Entity( name = "D" ) + @Table( name = "D" ) + public static class D { + @Id + @Column( name = "did") + public Integer id; + } +}