HHH-15111 MappingException is thrown for @JoinColumn with referencedColumnName on a @SecondaryTable
This commit is contained in:
parent
cd78676608
commit
1d67993173
|
@ -69,6 +69,7 @@ import org.hibernate.cfg.PropertyData;
|
||||||
import org.hibernate.cfg.QuerySecondPass;
|
import org.hibernate.cfg.QuerySecondPass;
|
||||||
import org.hibernate.cfg.RecoverableException;
|
import org.hibernate.cfg.RecoverableException;
|
||||||
import org.hibernate.cfg.SecondPass;
|
import org.hibernate.cfg.SecondPass;
|
||||||
|
import org.hibernate.cfg.SecondaryTableFromAnnotationSecondPass;
|
||||||
import org.hibernate.cfg.SecondaryTableSecondPass;
|
import org.hibernate.cfg.SecondaryTableSecondPass;
|
||||||
import org.hibernate.cfg.SetBasicValueTypeSecondPass;
|
import org.hibernate.cfg.SetBasicValueTypeSecondPass;
|
||||||
import org.hibernate.cfg.UniqueConstraintHolder;
|
import org.hibernate.cfg.UniqueConstraintHolder;
|
||||||
|
@ -1591,6 +1592,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
private ArrayList<FkSecondPass> fkSecondPassList;
|
private ArrayList<FkSecondPass> fkSecondPassList;
|
||||||
private ArrayList<CreateKeySecondPass> createKeySecondPasList;
|
private ArrayList<CreateKeySecondPass> createKeySecondPasList;
|
||||||
private ArrayList<SecondaryTableSecondPass> secondaryTableSecondPassList;
|
private ArrayList<SecondaryTableSecondPass> secondaryTableSecondPassList;
|
||||||
|
private ArrayList<SecondaryTableFromAnnotationSecondPass> secondaryTableFromAnnotationSecondPassesList;
|
||||||
private ArrayList<QuerySecondPass> querySecondPassList;
|
private ArrayList<QuerySecondPass> querySecondPassList;
|
||||||
private ArrayList<ImplicitColumnNamingSecondPass> implicitColumnNamingSecondPassList;
|
private ArrayList<ImplicitColumnNamingSecondPass> implicitColumnNamingSecondPassList;
|
||||||
|
|
||||||
|
@ -1618,6 +1620,12 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
else if ( secondPass instanceof SecondaryTableSecondPass ) {
|
else if ( secondPass instanceof SecondaryTableSecondPass ) {
|
||||||
addSecondaryTableSecondPass( (SecondaryTableSecondPass) secondPass, onTopOfTheQueue );
|
addSecondaryTableSecondPass( (SecondaryTableSecondPass) secondPass, onTopOfTheQueue );
|
||||||
}
|
}
|
||||||
|
else if ( secondPass instanceof SecondaryTableFromAnnotationSecondPass ) {
|
||||||
|
addSecondaryTableFromAnnotationSecondPass(
|
||||||
|
(SecondaryTableFromAnnotationSecondPass) secondPass,
|
||||||
|
onTopOfTheQueue
|
||||||
|
);
|
||||||
|
}
|
||||||
else if ( secondPass instanceof QuerySecondPass ) {
|
else if ( secondPass instanceof QuerySecondPass ) {
|
||||||
addQuerySecondPass( (QuerySecondPass) secondPass, onTopOfTheQueue );
|
addQuerySecondPass( (QuerySecondPass) secondPass, onTopOfTheQueue );
|
||||||
}
|
}
|
||||||
|
@ -1677,6 +1685,13 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
addSecondPass( secondPass, secondaryTableSecondPassList, onTopOfTheQueue );
|
addSecondPass( secondPass, secondaryTableSecondPassList, onTopOfTheQueue );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addSecondaryTableFromAnnotationSecondPass(SecondaryTableFromAnnotationSecondPass secondPass, boolean onTopOfTheQueue){
|
||||||
|
if ( secondaryTableFromAnnotationSecondPassesList == null ) {
|
||||||
|
secondaryTableFromAnnotationSecondPassesList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
addSecondPass( secondPass, secondaryTableFromAnnotationSecondPassesList, onTopOfTheQueue );
|
||||||
|
}
|
||||||
|
|
||||||
private void addQuerySecondPass(QuerySecondPass secondPass, boolean onTopOfTheQueue) {
|
private void addQuerySecondPass(QuerySecondPass secondPass, boolean onTopOfTheQueue) {
|
||||||
if ( querySecondPassList == null ) {
|
if ( querySecondPassList == null ) {
|
||||||
querySecondPassList = new ArrayList<>();
|
querySecondPassList = new ArrayList<>();
|
||||||
|
@ -1765,6 +1780,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
|
|
||||||
private void processFkSecondPassesInOrder() {
|
private void processFkSecondPassesInOrder() {
|
||||||
if ( fkSecondPassList == null || fkSecondPassList.isEmpty() ) {
|
if ( fkSecondPassList == null || fkSecondPassList.isEmpty() ) {
|
||||||
|
processSecondPasses( secondaryTableFromAnnotationSecondPassesList );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1798,6 +1814,8 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
sp.doSecondPass( getEntityBindingMap() );
|
sp.doSecondPass( getEntityBindingMap() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processSecondPasses( secondaryTableFromAnnotationSecondPassesList );
|
||||||
|
|
||||||
processEndOfQueue( endOfQueueFkSecondPasses );
|
processEndOfQueue( endOfQueueFkSecondPasses );
|
||||||
|
|
||||||
fkSecondPassList.clear();
|
fkSecondPassList.clear();
|
||||||
|
|
|
@ -705,6 +705,8 @@ public final class AnnotationBinder {
|
||||||
context.getMetadataCollector().addEntityBinding( persistentClass );
|
context.getMetadataCollector().addEntityBinding( persistentClass );
|
||||||
|
|
||||||
//Process secondary tables and complementary definitions (ie o.h.a.Table)
|
//Process secondary tables and complementary definitions (ie o.h.a.Table)
|
||||||
|
context.getMetadataCollector()
|
||||||
|
.addSecondPass( new SecondaryTableFromAnnotationSecondPass( entityBinder, propertyHolder, clazzToProcess ) );
|
||||||
context.getMetadataCollector()
|
context.getMetadataCollector()
|
||||||
.addSecondPass( new SecondaryTableSecondPass( entityBinder, propertyHolder, clazzToProcess ) );
|
.addSecondPass( new SecondaryTableSecondPass( entityBinder, propertyHolder, clazzToProcess ) );
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.hibernate.cfg;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.MappingException;
|
||||||
|
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||||
|
import org.hibernate.cfg.annotations.EntityBinder;
|
||||||
|
import org.hibernate.mapping.PersistentClass;
|
||||||
|
|
||||||
|
public class SecondaryTableFromAnnotationSecondPass implements SecondPass{
|
||||||
|
private final EntityBinder entityBinder;
|
||||||
|
private final PropertyHolder propertyHolder;
|
||||||
|
private final XAnnotatedElement annotatedClass;
|
||||||
|
|
||||||
|
public SecondaryTableFromAnnotationSecondPass(EntityBinder entityBinder, PropertyHolder propertyHolder, XAnnotatedElement annotatedClass) {
|
||||||
|
this.entityBinder = entityBinder;
|
||||||
|
this.propertyHolder = propertyHolder;
|
||||||
|
this.annotatedClass = annotatedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
|
||||||
|
entityBinder.finalSecondaryTableFromAnnotationBinding( propertyHolder );
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import jakarta.persistence.Access;
|
import jakarta.persistence.Access;
|
||||||
import jakarta.persistence.Cacheable;
|
import jakarta.persistence.Cacheable;
|
||||||
import jakarta.persistence.ConstraintMode;
|
import jakarta.persistence.ConstraintMode;
|
||||||
|
@ -125,6 +127,9 @@ public class EntityBinder {
|
||||||
// atm we use both from here; HBM binding solely uses InFlightMetadataCollector.EntityTableXref
|
// atm we use both from here; HBM binding solely uses InFlightMetadataCollector.EntityTableXref
|
||||||
private final java.util.Map<String, Join> secondaryTables = new HashMap<>();
|
private final java.util.Map<String, Join> secondaryTables = new HashMap<>();
|
||||||
private final java.util.Map<String, Object> secondaryTableJoins = new HashMap<>();
|
private final java.util.Map<String, Object> secondaryTableJoins = new HashMap<>();
|
||||||
|
private final java.util.Map<String, Join> secondaryTablesFromAnnotation = new HashMap<>();
|
||||||
|
private final java.util.Map<String, Object> secondaryTableFromAnnotationJoins = new HashMap<>();
|
||||||
|
|
||||||
private final List<Filter> filters = new ArrayList<>();
|
private final List<Filter> filters = new ArrayList<>();
|
||||||
private boolean ignoreIdAnnotations;
|
private boolean ignoreIdAnnotations;
|
||||||
private AccessType propertyAccessType = AccessType.DEFAULT;
|
private AccessType propertyAccessType = AccessType.DEFAULT;
|
||||||
|
@ -813,13 +818,28 @@ public class EntityBinder {
|
||||||
* Those operations has to be done after the id definition of the persistence class.
|
* Those operations has to be done after the id definition of the persistence class.
|
||||||
* ie after the properties parsing
|
* ie after the properties parsing
|
||||||
*/
|
*/
|
||||||
Iterator<Join> joins = secondaryTables.values().iterator();
|
|
||||||
Iterator<Object> joinColumns = secondaryTableJoins.values().iterator();
|
Iterator<Object> joinColumns = secondaryTableJoins.values().iterator();
|
||||||
|
|
||||||
while ( joins.hasNext() ) {
|
for ( Map.Entry<String, Join> entrySet : secondaryTables.entrySet() ) {
|
||||||
Object uncastedColumn = joinColumns.next();
|
if ( !secondaryTablesFromAnnotation.containsKey( entrySet.getKey() ) ) {
|
||||||
Join join = joins.next();
|
Object uncastedColumn = joinColumns.next();
|
||||||
createPrimaryColumnsToSecondaryTable( uncastedColumn, propertyHolder, join );
|
createPrimaryColumnsToSecondaryTable( uncastedColumn, propertyHolder, entrySet.getValue() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finalSecondaryTableFromAnnotationBinding(PropertyHolder propertyHolder) {
|
||||||
|
/*
|
||||||
|
* Those operations have to be done before the end of the FK second pass processing in order
|
||||||
|
* to find the join columns belonging to secondary tables
|
||||||
|
*/
|
||||||
|
Iterator<Object> joinColumns = secondaryTableFromAnnotationJoins.values().iterator();
|
||||||
|
|
||||||
|
for ( Map.Entry<String, Join> entrySet : secondaryTables.entrySet() ) {
|
||||||
|
if ( secondaryTablesFromAnnotation.containsKey( entrySet.getKey() ) ) {
|
||||||
|
Object uncastedColumn = joinColumns.next();
|
||||||
|
createPrimaryColumnsToSecondaryTable( uncastedColumn, propertyHolder, entrySet.getValue() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1106,8 +1126,15 @@ public class EntityBinder {
|
||||||
createPrimaryColumnsToSecondaryTable( joinColumns, propertyHolder, join );
|
createPrimaryColumnsToSecondaryTable( joinColumns, propertyHolder, join );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
secondaryTables.put( table.getQuotedName(), join );
|
final String quotedName = table.getQuotedName();
|
||||||
secondaryTableJoins.put( table.getQuotedName(), joinColumns );
|
if ( secondaryTable != null ) {
|
||||||
|
secondaryTablesFromAnnotation.put( quotedName, join );
|
||||||
|
secondaryTableFromAnnotationJoins.put( quotedName, joinColumns );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
secondaryTableJoins.put( quotedName, joinColumns );
|
||||||
|
}
|
||||||
|
secondaryTables.put( quotedName, join );
|
||||||
}
|
}
|
||||||
|
|
||||||
return join;
|
return join;
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package org.hibernate.orm.test.annotations.joincolumn;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
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 jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.DiscriminatorColumn;
|
||||||
|
import jakarta.persistence.DiscriminatorValue;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.SecondaryTable;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
JoinColumnWithSecondaryTableTest.Being.class,
|
||||||
|
JoinColumnWithSecondaryTableTest.Animal.class,
|
||||||
|
JoinColumnWithSecondaryTableTest.Cat.class,
|
||||||
|
JoinColumnWithSecondaryTableTest.Toy.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
@TestForIssue( jiraKey = "HHH-15111")
|
||||||
|
public class JoinColumnWithSecondaryTableTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIt(SessionFactoryScope scope){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "being")
|
||||||
|
@Inheritance
|
||||||
|
@DiscriminatorColumn(name = "type")
|
||||||
|
public static abstract class Being {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@SecondaryTable(name = "animal")
|
||||||
|
public static abstract class Animal extends Being {
|
||||||
|
@Column(name = "uuid", table = "animal")
|
||||||
|
private String uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@SecondaryTable(name = "cat")
|
||||||
|
@DiscriminatorValue(value = "CAT")
|
||||||
|
public static class Cat extends Animal {
|
||||||
|
@Column(name = "name", table = "cat")
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public static class Toy {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "animal_uuid", referencedColumnName = "uuid")
|
||||||
|
private Cat cat;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue