Fix UnionSublcass polymorphic associations

This commit is contained in:
Andrea Boriero 2020-07-09 18:21:06 +01:00
parent 5b82cd59d7
commit 01b24089c2
13 changed files with 394 additions and 38 deletions

View File

@ -617,7 +617,8 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
final TableReference primaryTableReference = entityPartDescriptor.getEntityMappingType()
final EntityMappingType entityMappingType = entityPartDescriptor.getEntityMappingType();
final TableReference primaryTableReference = entityMappingType
.createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver,
@ -630,9 +631,9 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
lockMode,
primaryTableReference,
sqlAliasBase,
(tableExpression) -> entityPartDescriptor.getEntityMappingType()
(tableExpression) -> entityMappingType
.containsTableReference( tableExpression ),
(tableExpression, tg) -> entityPartDescriptor.getEntityMappingType().createTableReferenceJoin(
(tableExpression, tg) -> entityMappingType.createTableReferenceJoin(
tableExpression,
sqlAliasBase,
primaryTableReference,

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@ -163,10 +162,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) {
final String rhsTableExpression = rhs.getTableExpression();
final String lhsTableExpression = lhs.getTableExpression();
if ( lhsTableExpression.equals( keyColumnContainingTable ) ) {
assert rhsTableExpression.equals( targetColumnContainingTable );
if ( lhs.getTableReference( keyColumnContainingTable ) != null ) {
return new ComparisonPredicate(
new ColumnReference(
lhs,
@ -184,7 +180,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
);
}
else {
assert rhsTableExpression.equals( keyColumnContainingTable );
return new ComparisonPredicate(
new ColumnReference(
lhs,

View File

@ -51,6 +51,7 @@ import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
@ -233,13 +234,20 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
SqlAstCreationContext creationContext) {
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
final TableReference tableReference = new TableReference(
final TableReference tableReference = resolvePrimaryTableReference(sqlAliasBase);
return new UnionTableGroup( navigablePath, tableReference, this );
}
@Override
protected TableReference resolvePrimaryTableReference(SqlAliasBase sqlAliasBase) {
return new UnionTableReference(
getTableName(),
subclassSpaces,
sqlAliasBase.generateNewAlias(),
false,
getFactory()
);
return new UnionTableGroup( navigablePath, tableReference, this );
}
@Override

View File

@ -26,7 +26,7 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
@Override
public TableReference resolveTableReference(String tableExpression, Supplier<TableReference> creator) {
final TableReference existing = resolveTableReferenceInternal( tableExpression );
final TableReference existing = getTableReferenceInternal( tableExpression );
if ( existing != null ) {
return existing;
}
@ -38,7 +38,7 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
public TableReference resolveTableReference(String tableExpression) {
assert tableExpression != null;
final TableReference tableReference = resolveTableReferenceInternal( tableExpression );
final TableReference tableReference = getTableReferenceInternal( tableExpression );
if ( tableReference == null ) {
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
}
@ -48,16 +48,16 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
@Override
public TableReference getTableReference(String tableExpression) {
return resolveTableReferenceInternal( tableExpression );
return getTableReferenceInternal( tableExpression );
}
protected TableReference resolveTableReferenceInternal(String tableExpression) {
if ( getPrimaryTableReference().getTableExpression().equals( tableExpression ) ) {
protected TableReference getTableReferenceInternal(String tableExpression) {
if ( getPrimaryTableReference().getTableReference( tableExpression ) != null) {
return getPrimaryTableReference();
}
for ( TableReferenceJoin tableJoin : getTableReferenceJoins() ) {
if ( tableJoin.getJoinedTableReference().getTableExpression().equals( tableExpression ) ) {
if ( tableJoin.getJoinedTableReference().getTableReference( tableExpression ) != null) {
return tableJoin.getJoinedTableReference();
}
}

View File

@ -104,12 +104,7 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
@Override
public void setTableGroupJoins(Set<TableGroupJoin> joins) {
if ( tableGroupJoins == null ) {
tableGroupJoins = new HashSet<>( joins );
}
else {
tableGroupJoins.addAll( joins );
}
tableGroupJoins = new HashSet<>( joins );
}
@Override

View File

@ -75,12 +75,7 @@ public class CompositeTableGroup implements VirtualTableGroup {
@Override
public void setTableGroupJoins(Set<TableGroupJoin> joins) {
if ( tableGroupJoins == null ) {
tableGroupJoins = new HashSet<>( joins );
}
else {
tableGroupJoins.addAll( joins );
}
tableGroupJoins = new HashSet<>( joins );
}
@Override

View File

@ -94,8 +94,8 @@ public class StandardTableGroup extends AbstractTableGroup {
}
@Override
public TableReference resolveTableReferenceInternal(String tableExpression) {
final TableReference tableReference = super.resolveTableReferenceInternal( tableExpression );
public TableReference getTableReferenceInternal(String tableExpression) {
final TableReference tableReference = super.getTableReferenceInternal( tableExpression );
if ( tableReference != null ) {
return tableReference;
}
@ -105,7 +105,7 @@ public class StandardTableGroup extends AbstractTableGroup {
for ( int i = 0; i < tableJoins.size(); i++ ) {
final TableReferenceJoin join = tableJoins.get( i );
assert join != null;
if ( join.getJoinedTableReference().getTableExpression().equals( tableExpression ) ) {
if ( join.getJoinedTableReference().getTableReference( tableExpression ) != null ) {
return join.getJoinedTableReference();
}
}
@ -116,7 +116,7 @@ public class StandardTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference();
if ( primaryTableReference.getTableExpression().equals( tableExpression ) ) {
if ( primaryTableReference.getTableReference( tableExpression ) != null ) {
return primaryTableReference;
}
}

View File

@ -53,6 +53,7 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
void applyAffectedTableNames(Consumer<String> nameCollector);
TableReference getPrimaryTableReference();
List<TableReferenceJoin> getTableReferenceJoins();
@Override

View File

@ -61,7 +61,7 @@ public class TableReference implements SqlAstNode, ColumnReferenceQualifier {
if ( tableExpression.equals( getTableExpression() ) ) {
return this;
}
return null;
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
}
@Override

View File

@ -7,6 +7,7 @@
package org.hibernate.sql.ast.tree.from;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@ -23,6 +24,7 @@ import org.hibernate.query.NavigablePath;
*/
public class UnionTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath;
private Set<TableGroupJoin> tableGroupJoins;
private final UnionSubclassEntityPersister modelPart;
private final TableReference tableReference;
@ -63,24 +65,32 @@ public class UnionTableGroup implements VirtualTableGroup {
@Override
public Set<TableGroupJoin> getTableGroupJoins() {
return Collections.emptySet();
return tableGroupJoins == null ? Collections.emptySet() : Collections.unmodifiableSet( tableGroupJoins );
}
@Override
public boolean hasTableGroupJoins() {
return false;
return tableGroupJoins != null && !tableGroupJoins.isEmpty();
}
@Override
public void setTableGroupJoins(Set<TableGroupJoin> joins) {
tableGroupJoins = new HashSet<>( joins );
}
@Override
public void addTableGroupJoin(TableGroupJoin join) {
if ( tableGroupJoins == null ) {
tableGroupJoins = new HashSet<>();
}
tableGroupJoins.add( join );
}
@Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
tableGroupJoins.forEach( consumer );
}
}
@Override

View File

@ -0,0 +1,54 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.sql.ast.tree.from;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* @author Andrea Boriero
*/
public class UnionTableReference extends TableReference {
private final String[] subclassTableSpaceExpressions;
public UnionTableReference(
String unionTableExpression,
String[] subclassTableSpaceExpressions,
String identificationVariable,
boolean isOptional,
SessionFactoryImplementor sessionFactory) {
super( unionTableExpression, identificationVariable, isOptional, sessionFactory );
this.subclassTableSpaceExpressions = subclassTableSpaceExpressions;
}
public TableReference resolveTableReference(String tableExpression) {
if ( hasTableExpression( tableExpression ) ) {
return this;
}
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
}
@Override
public TableReference getTableReference(String tableExpression) {
if ( hasTableExpression( tableExpression ) ) {
return this;
}
return null;
}
private boolean hasTableExpression(String tableExpression) {
if ( tableExpression.equals( getTableExpression() ) ) {
return true;
}
for ( String expression : subclassTableSpaceExpressions ) {
if ( tableExpression.equals( expression ) ) {
return true;
}
}
return false;
}
}

View File

@ -128,8 +128,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
for ( int i = 0; i < identifierInitializers.size(); i++ ) {
final Initializer existing = identifierInitializers.get( i );
if ( existing.getNavigablePath().equals( navigablePath )
&& fetchedModelPart.getNavigableRole().equals(
existing.getInitializedPart().getNavigableRole() ) ) {
&& fetchedModelPart.getNavigableRole()
.equals( existing.getInitializedPart().getNavigableRole() ) ) {
return existing;
}
}

View File

@ -0,0 +1,297 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.inheritance.discriminator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
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.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @author Davide D'Alto
*/
@TestForIssue(jiraKey = "HHH-12332")
@DomainModel(
annotatedClasses = {
TablePerClassInheritanceTest.Person.class,
TablePerClassInheritanceTest.Child.class,
TablePerClassInheritanceTest.Man.class,
TablePerClassInheritanceTest.Woman.class
}
)
@SessionFactory
public class TablePerClassInheritanceTest {
private final Man john = new Man( "John", "Riding Roller Coasters" );
private final Woman jane = new Woman( "Jane", "Hippotherapist" );
private final Child susan = new Child( "Susan", "Super Mario retro Mushroom" );
private final Child mark = new Child( "Mark", "Fidget Spinner" );
private final List<Child> children = new ArrayList<>( Arrays.asList( susan, mark ) );
private final List<Person> familyMembers = Arrays.asList( john, jane, susan, mark );
@BeforeEach
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
jane.setHusband( john );
jane.setChildren( children );
john.setWife( jane );
john.setChildren( children );
for ( Child child : children ) {
child.setFather( john );
child.setMother( jane );
}
for ( Person person : familyMembers ) {
session.persist( person );
}
} );
}
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
List<Person> people = session.createQuery( "FROM Person p", Person.class ).getResultList();
people.forEach( person -> session.delete( person ) );
}
);
}
@Test
public void testSelectHierarchyRoot(SessionFactoryScope scope) {
scope.inTransaction( session -> {
List<Person> people = session.createQuery( "FROM Person p", Person.class ).getResultList();
assertThat( people.size(), is( familyMembers.size() ) );
for ( Person person : people ) {
if ( person instanceof Man ) {
assertThat( ( (Man) person ).getHobby(), is( john.getHobby() ) );
}
else if ( person instanceof Woman ) {
assertThat( ( (Woman) person ).getJob(), is( jane.getJob() ) );
}
else if ( person instanceof Child ) {
if ( person.getName().equals( "Susan" ) ) {
assertThat( ( (Child) person ).getFavouriteToy(), is( susan.getFavouriteToy() ) );
}
else {
assertThat( ( (Child) person ).getFavouriteToy(), is( mark.getFavouriteToy() ) );
}
}
else {
fail( "Unexpected result: " + person );
}
}
} );
}
@Test
public void testSelectHierarchyLeaf(SessionFactoryScope scope) {
scope.inTransaction( session -> {
List<Man> men = session.createQuery( "FROM Man m", Man.class ).getResultList();
for ( Man man : men ) {
assertThat( man.getHobby(), is( john.getHobby() ) );
}
} );
}
@Test
public void testGetHierarchyLeaf(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Man man = session.get( Man.class, "John" );
assertThat( man.getHobby(), is( john.getHobby() ) );
} );
}
@Entity(name = "Person")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
public static class Person {
@Id
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
@Entity(name = "Child")
@DiscriminatorValue("CHILD")
public static class Child extends Person {
private String favouriteToy;
@OneToOne
private Woman mother;
@OneToOne
private Man father;
public Child() {
}
public Child(String name, String favouriteToy) {
super( name );
this.favouriteToy = favouriteToy;
}
public String getFavouriteToy() {
return favouriteToy;
}
public void setFavouriteToy(String favouriteToy) {
this.favouriteToy = favouriteToy;
}
public Man getFather() {
return father;
}
public void setFather(Man father) {
this.father = father;
}
public Woman getMother() {
return mother;
}
public void setMother(Woman mother) {
this.mother = mother;
}
}
@Entity(name = "Man")
@DiscriminatorValue("MAN")
public static class Man extends Person {
private String hobby;
@OneToOne
private Woman wife;
@OneToMany(mappedBy = "father")
private List<Child> children = new ArrayList<>();
public Man() {
}
public Man(String name, String hobby) {
super( name );
this.hobby = hobby;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public Woman getWife() {
return wife;
}
public void setWife(Woman wife) {
this.wife = wife;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
}
@Entity(name = "Woman")
@DiscriminatorValue("WOMAN")
public static class Woman extends Person {
private String job;
@OneToOne
private Man husband;
@OneToMany(mappedBy = "mother")
private List<Child> children = new ArrayList<>();
public Woman() {
}
public Woman(String name, String job) {
super( name );
this.job = job;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Man getHusband() {
return husband;
}
public void setHusband(Man husband) {
this.husband = husband;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
}
}