Skip to content

Commit ddd7ffe

Browse files
committed
proof of concept for formula support in @manytomany
There is currently no way to do <many-to-many formula="..."/> in annotations. This lets you write @manytomany @JoinFormula="..."
1 parent cf65a2d commit ddd7ffe

File tree

4 files changed

+147
-9
lines changed

4 files changed

+147
-9
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumn.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,26 @@ static AnnotatedJoinColumn buildExplicitJoinTableJoinColumn(
433433
return column;
434434
}
435435

436+
static AnnotatedJoinColumn buildExplicitJoinTableJoinFormula(
437+
AnnotatedJoinColumns parent,
438+
PropertyHolder propertyHolder,
439+
PropertyData inferredData,
440+
JoinFormula joinFormula) {
441+
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
442+
column.setImplicit( true );
443+
// column.setPropertyHolder( propertyHolder );
444+
// column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
445+
// column.setJoins( secondaryTables );
446+
// column.setContext( context );
447+
column.setNullable( false ); //I break the spec, but it's for good
448+
//done after the annotation to override it
449+
column.setParent( parent );
450+
column.setFormula( joinFormula.value() );
451+
column.setReferencedColumn( joinFormula.referencedColumnName() );
452+
column.bind();
453+
return column;
454+
}
455+
436456
@Override
437457
public String toString() {
438458
final StringBuilder string = new StringBuilder();

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumns.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,26 @@ public static AnnotatedJoinColumns buildJoinTableJoinColumns(
209209
return parent;
210210
}
211211

212+
/**
213+
* Called for join tables in {@link jakarta.persistence.ManyToMany} associations.
214+
*/
215+
public static AnnotatedJoinColumns buildJoinTableJoinFormula(
216+
JoinFormula joinFormula,
217+
Map<String, Join> secondaryTables,
218+
PropertyHolder propertyHolder,
219+
PropertyData inferredData,
220+
String mappedBy,
221+
MetadataBuildingContext context) {
222+
final AnnotatedJoinColumns parent = new AnnotatedJoinColumns();
223+
parent.setBuildingContext( context );
224+
parent.setJoins( secondaryTables );
225+
parent.setPropertyHolder( propertyHolder );
226+
parent.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) );
227+
parent.setMappedBy( mappedBy );
228+
AnnotatedJoinColumn.buildExplicitJoinTableJoinFormula( parent, propertyHolder, inferredData, joinFormula );
229+
return parent;
230+
}
231+
212232
public List<AnnotatedJoinColumn> getJoinColumns() {
213233
return columns;
214234
}

hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.hibernate.annotations.Formula;
4141
import org.hibernate.annotations.HQLSelect;
4242
import org.hibernate.annotations.Immutable;
43+
import org.hibernate.annotations.JoinFormula;
4344
import org.hibernate.annotations.LazyCollection;
4445
import org.hibernate.annotations.LazyCollectionOption;
4546
import org.hibernate.annotations.LazyGroup;
@@ -149,6 +150,7 @@
149150
import static org.hibernate.boot.model.internal.AnnotatedColumn.buildFormulaFromAnnotation;
150151
import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinColumnsWithDefaultColumnSuffix;
151152
import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinTableJoinColumns;
153+
import static org.hibernate.boot.model.internal.AnnotatedJoinColumns.buildJoinTableJoinFormula;
152154
import static org.hibernate.boot.model.internal.BinderHelper.buildAnyValue;
153155
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
154156
import static org.hibernate.boot.model.internal.BinderHelper.createSyntheticPropertyReference;
@@ -641,6 +643,7 @@ private static void bindJoinedTableAssociation(
641643
final CollectionTable collectionTable = property.getAnnotation( CollectionTable.class );
642644
final JoinColumn[] annJoins;
643645
final JoinColumn[] annInverseJoins;
646+
final JoinFormula annJoinFormula;
644647
if ( assocTable != null || collectionTable != null ) {
645648

646649
final String catalog;
@@ -689,10 +692,25 @@ private static void bindJoinedTableAssociation(
689692
//set check constraint in the second pass
690693
annJoins = joins.length == 0 ? null : joins;
691694
annInverseJoins = inverseJoins == null || inverseJoins.length == 0 ? null : inverseJoins;
695+
annJoinFormula = null;
692696
}
693697
else {
698+
if ( property.isAnnotationPresent(JoinFormula.class) ) {
699+
annJoinFormula = property.getAnnotation(JoinFormula.class);
700+
}
701+
else {
702+
annJoinFormula = null;
703+
}
704+
if ( property.isAnnotationPresent(JoinColumns.class) ) {
705+
annInverseJoins = property.getAnnotation(JoinColumns.class).value();
706+
}
707+
else if ( property.isAnnotationPresent(JoinColumn.class) ) {
708+
annInverseJoins = new JoinColumn[] { property.getAnnotation(JoinColumn.class) };
709+
}
710+
else {
711+
annInverseJoins = null;
712+
}
694713
annJoins = null;
695-
annInverseJoins = null;
696714
}
697715
associationTableBinder.setBuildingContext( buildingContext );
698716
collectionBinder.setTableBinder( associationTableBinder );
@@ -704,14 +722,26 @@ private static void bindJoinedTableAssociation(
704722
mappedBy,
705723
buildingContext
706724
) );
707-
collectionBinder.setInverseJoinColumns( buildJoinTableJoinColumns(
708-
annInverseJoins,
709-
entityBinder.getSecondaryTables(),
710-
propertyHolder,
711-
inferredData,
712-
mappedBy,
713-
buildingContext
714-
) );
725+
if ( annJoinFormula != null ) {
726+
collectionBinder.setInverseJoinColumns( buildJoinTableJoinFormula(
727+
annJoinFormula,
728+
entityBinder.getSecondaryTables(),
729+
propertyHolder,
730+
inferredData,
731+
mappedBy,
732+
buildingContext
733+
) );
734+
}
735+
else {
736+
collectionBinder.setInverseJoinColumns( buildJoinTableJoinColumns(
737+
annInverseJoins,
738+
entityBinder.getSecondaryTables(),
739+
propertyHolder,
740+
inferredData,
741+
mappedBy,
742+
buildingContext
743+
) );
744+
}
715745
}
716746

717747
protected MetadataBuildingContext getBuildingContext() {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.orm.test.annotations.formula;
8+
9+
import jakarta.persistence.Column;
10+
import jakarta.persistence.Entity;
11+
import jakarta.persistence.Id;
12+
import jakarta.persistence.ManyToMany;
13+
import jakarta.persistence.Table;
14+
import org.hibernate.annotations.JoinFormula;
15+
import org.hibernate.testing.orm.junit.DomainModel;
16+
import org.hibernate.testing.orm.junit.SessionFactory;
17+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
18+
import org.junit.jupiter.api.Test;
19+
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
25+
@SessionFactory
26+
@DomainModel(annotatedClasses = {JoinColumnOrFormula2Test.A.class, JoinColumnOrFormula2Test.D.class})
27+
public class JoinColumnOrFormula2Test {
28+
@Test
29+
public void test(SessionFactoryScope scope) {
30+
scope.inTransaction(s -> {
31+
A a = new A();
32+
a.id = 3;
33+
a.dId = 5;
34+
D d = new D();
35+
d.id = 5;
36+
s.persist(a);
37+
s.persist(d);
38+
a.ds.add(d);
39+
});
40+
scope.inSession(s -> {
41+
Set<D> ds = s.get(A.class, 3).ds;
42+
assertEquals(1, ds.size());
43+
});
44+
}
45+
46+
@Entity( name = "A" )
47+
@Table( name = "A" )
48+
public static class A {
49+
@Id
50+
@Column( name = "aid")
51+
public Integer id;
52+
53+
@Column( name = "did")
54+
public Integer dId;
55+
56+
@ManyToMany
57+
@JoinFormula(value = "A_aid+2", referencedColumnName = "did")
58+
Set<D> ds = new HashSet<>();
59+
}
60+
61+
@Entity( name = "D" )
62+
@Table( name = "D" )
63+
public static class D {
64+
@Id
65+
@Column( name = "did")
66+
public Integer id;
67+
}
68+
}

0 commit comments

Comments
 (0)