HHH-16123 Add another test and fix rendering the pruned subquery in UnionSubclassEntityPersister
This commit is contained in:
parent
6fc3ec6901
commit
1d6951aac3
|
@ -8,6 +8,7 @@ package org.hibernate.persister.entity;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -39,6 +40,7 @@ import org.hibernate.mapping.PersistentClass;
|
||||||
import org.hibernate.mapping.Subclass;
|
import org.hibernate.mapping.Subclass;
|
||||||
import org.hibernate.mapping.Table;
|
import org.hibernate.mapping.Table;
|
||||||
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
import org.hibernate.metamodel.mapping.TableDetails;
|
import org.hibernate.metamodel.mapping.TableDetails;
|
||||||
|
@ -528,26 +530,16 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
||||||
|
|
||||||
// Collect all selectables of every entity subtype and group by selection expression as well as table name
|
// Collect all selectables of every entity subtype and group by selection expression as well as table name
|
||||||
final LinkedHashMap<String, Map<String, SelectableMapping>> selectables = new LinkedHashMap<>();
|
final LinkedHashMap<String, Map<String, SelectableMapping>> selectables = new LinkedHashMap<>();
|
||||||
final SelectableConsumer selectableConsumer = (i, selectable) ->
|
|
||||||
selectables.computeIfAbsent( selectable.getSelectionExpression(), k -> new HashMap<>() )
|
|
||||||
.put( selectable.getContainingTableExpression(), selectable );
|
|
||||||
// Collect the concrete subclass table names for the treated entity names
|
// Collect the concrete subclass table names for the treated entity names
|
||||||
final Set<String> treatedTableNames = new HashSet<>( treated.size() );
|
final Set<String> treatedTableNames = new HashSet<>( treated.size() );
|
||||||
for ( String subclassName : treated ) {
|
for ( String subclassName : treated ) {
|
||||||
final UnionSubclassEntityPersister subPersister =
|
final UnionSubclassEntityPersister subPersister =
|
||||||
(UnionSubclassEntityPersister) metamodel.getEntityDescriptor( subclassName );
|
(UnionSubclassEntityPersister) metamodel.getEntityDescriptor( subclassName );
|
||||||
for ( String subclassTableName : subPersister.getSubclassTableNames() ) {
|
// Collect all the real (non-abstract) table names
|
||||||
if ( ArrayHelper.indexOf( subclassSpaces, subclassTableName ) != -1 ) {
|
treatedTableNames.addAll( Arrays.asList( subPersister.getConstraintOrderedTableNameClosure() ) );
|
||||||
treatedTableNames.add( subclassTableName );
|
// Collect selectables grouped by the table names in which they appear
|
||||||
}
|
// TODO: we could cache this
|
||||||
}
|
subPersister.collectSelectableOwners( selectables );
|
||||||
subPersister.getIdentifierMapping().forEachSelectable( selectableConsumer );
|
|
||||||
if ( subPersister.getVersionMapping() != null ) {
|
|
||||||
subPersister.getVersionMapping().forEachSelectable( selectableConsumer );
|
|
||||||
}
|
|
||||||
subPersister.visitSubTypeAttributeMappings(
|
|
||||||
attributeMapping -> attributeMapping.forEachSelectable( selectableConsumer )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a union sub-query for the table names, like generateSubquery(PersistentClass model, Mapping mapping)
|
// Create a union sub-query for the table names, like generateSubquery(PersistentClass model, Mapping mapping)
|
||||||
|
@ -555,8 +547,8 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
||||||
.append( "( " );
|
.append( "( " );
|
||||||
final StringBuilderSqlAppender sqlAppender = new StringBuilderSqlAppender( buf );
|
final StringBuilderSqlAppender sqlAppender = new StringBuilderSqlAppender( buf );
|
||||||
|
|
||||||
for ( String name : getSubclassEntityNames() ) {
|
for ( EntityMappingType mappingType : getSubMappingTypes() ) {
|
||||||
final AbstractEntityPersister persister = (AbstractEntityPersister) metamodel.findEntityDescriptor( name );
|
final AbstractEntityPersister persister = (AbstractEntityPersister) mappingType;
|
||||||
final String subclassTableName = persister.getTableName();
|
final String subclassTableName = persister.getTableName();
|
||||||
if ( treatedTableNames.contains( subclassTableName ) ) {
|
if ( treatedTableNames.contains( subclassTableName ) ) {
|
||||||
if ( buf.length() > 2 ) {
|
if ( buf.length() > 2 ) {
|
||||||
|
@ -587,6 +579,34 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
||||||
return buf.append( " )" ).toString();
|
return buf.append( " )" ).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void collectSelectableOwners(LinkedHashMap<String, Map<String, SelectableMapping>> selectables) {
|
||||||
|
if ( isAbstract() ) {
|
||||||
|
for ( EntityMappingType subMappingType : getSubMappingTypes() ) {
|
||||||
|
if ( !subMappingType.isAbstract() ) {
|
||||||
|
( (UnionSubclassEntityPersister) subMappingType ).collectSelectableOwners( selectables );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final SelectableConsumer selectableConsumer = (i, selectable) -> {
|
||||||
|
Map<String, SelectableMapping> selectableMapping = selectables.computeIfAbsent(
|
||||||
|
selectable.getSelectionExpression(),
|
||||||
|
k -> new HashMap<>()
|
||||||
|
);
|
||||||
|
selectableMapping.put( getTableName(), selectable );
|
||||||
|
};
|
||||||
|
getIdentifierMapping().forEachSelectable( selectableConsumer );
|
||||||
|
if ( getVersionMapping() != null ) {
|
||||||
|
getVersionMapping().forEachSelectable( selectableConsumer );
|
||||||
|
}
|
||||||
|
final AttributeMappingsList attributeMappings = getAttributeMappings();
|
||||||
|
final int size = attributeMappings.size();
|
||||||
|
for ( int i = 0; i < size; i++ ) {
|
||||||
|
attributeMappings.get( i ).forEachSelectable( selectableConsumer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getSubclassTableKeyColumns(int j) {
|
protected String[] getSubclassTableKeyColumns(int j) {
|
||||||
if ( j != 0 ) {
|
if ( j != 0 ) {
|
||||||
|
|
|
@ -28,12 +28,14 @@ import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.Tuple;
|
||||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
import jakarta.persistence.criteria.CriteriaQuery;
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
import jakarta.persistence.criteria.Join;
|
import jakarta.persistence.criteria.Join;
|
||||||
import jakarta.persistence.criteria.Root;
|
import jakarta.persistence.criteria.Root;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Marco Belladelli
|
* @author Marco Belladelli
|
||||||
|
@ -42,6 +44,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
@DomainModel(annotatedClasses = {
|
@DomainModel(annotatedClasses = {
|
||||||
TreatAbstractSuperclassTest.LongBook.class,
|
TreatAbstractSuperclassTest.LongBook.class,
|
||||||
TreatAbstractSuperclassTest.ShortBook.class,
|
TreatAbstractSuperclassTest.ShortBook.class,
|
||||||
|
TreatAbstractSuperclassTest.Article.class,
|
||||||
TreatAbstractSuperclassTest.Author.class,
|
TreatAbstractSuperclassTest.Author.class,
|
||||||
TreatAbstractSuperclassTest.AuthorParticipation.class,
|
TreatAbstractSuperclassTest.AuthorParticipation.class,
|
||||||
})
|
})
|
||||||
|
@ -85,6 +88,25 @@ public class TreatAbstractSuperclassTest {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTreatMultiple(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
final CriteriaBuilder cb = session.getCriteriaBuilder();
|
||||||
|
final CriteriaQuery<Tuple> criteria = cb.createTupleQuery();
|
||||||
|
final Root<Publication> publicationRoot = criteria.from( Publication.class );
|
||||||
|
// Treat as nested abstract superclass
|
||||||
|
final Root<Book> bookRoot = cb.treat( publicationRoot, Book.class );
|
||||||
|
final Root<Article> articleRoot = cb.treat( publicationRoot, Article.class );
|
||||||
|
criteria.multiselect(
|
||||||
|
bookRoot.get( "title" ),
|
||||||
|
articleRoot.get( "reference" )
|
||||||
|
);
|
||||||
|
final Tuple tuple = session.createQuery( criteria ).getSingleResult();
|
||||||
|
assertEquals( "Dune", tuple.get( 0 ) );
|
||||||
|
assertNull( tuple.get( 1 ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSubclassJoin(SessionFactoryScope scope) {
|
public void testSubclassJoin(SessionFactoryScope scope) {
|
||||||
scope.inTransaction( session -> {
|
scope.inTransaction( session -> {
|
||||||
|
@ -122,8 +144,29 @@ public class TreatAbstractSuperclassTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Article")
|
||||||
|
public static class Article extends Publication {
|
||||||
|
private String reference;
|
||||||
|
|
||||||
|
public Article() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Article(String title) {
|
||||||
|
super( title );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReference() {
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReference(String reference) {
|
||||||
|
this.reference = reference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name = "Book")
|
@Entity(name = "Book")
|
||||||
public static abstract class Book extends Publication {
|
public static abstract class Book extends Publication {
|
||||||
|
private String isbn;
|
||||||
@OneToMany(mappedBy = "book", cascade = CascadeType.REMOVE)
|
@OneToMany(mappedBy = "book", cascade = CascadeType.REMOVE)
|
||||||
private List<AuthorParticipation> participations = new ArrayList<>();
|
private List<AuthorParticipation> participations = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -141,6 +184,7 @@ public class TreatAbstractSuperclassTest {
|
||||||
|
|
||||||
@Entity(name = "LongBook")
|
@Entity(name = "LongBook")
|
||||||
public static class LongBook extends Book {
|
public static class LongBook extends Book {
|
||||||
|
private int pageCount;
|
||||||
public LongBook() {
|
public LongBook() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +195,7 @@ public class TreatAbstractSuperclassTest {
|
||||||
|
|
||||||
@Entity(name = "ShortBook")
|
@Entity(name = "ShortBook")
|
||||||
public static class ShortBook extends Book {
|
public static class ShortBook extends Book {
|
||||||
|
private int readTime;
|
||||||
public ShortBook() {
|
public ShortBook() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue