Fix issue with collection @OrderBy

This commit is contained in:
Andrea Boriero 2020-08-21 07:58:24 +01:00
parent 5dfa67bd6f
commit ff1fc10454
19 changed files with 360 additions and 193 deletions

View File

@ -0,0 +1,186 @@
/*
* 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.metamodel.mapping.internal;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.SortOrder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ordering.ast.DomainPath;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SortSpecification;
/**
* @author Andrea Boriero
*/
public abstract class AbstractDomainPath implements DomainPath {
public static final String ELEMENT_TOKEN = "$element$";
@Override
public void apply(
QuerySpec ast,
TableGroup tableGroup,
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState) {
final SqlAstCreationContext creationContext = creationState.getCreationContext();
apply(
getReferenceModelPart(),
ast,
tableGroup,
collation,
modelPartName,
sortOrder,
creationState,
creationContext.getSessionFactory(),
creationState.getSqlExpressionResolver()
);
}
public void apply(
ModelPart referenceModelPart,
QuerySpec ast,
TableGroup tableGroup,
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState,
SessionFactoryImplementor sessionFactory,
SqlExpressionResolver sqlExprResolver) {
if ( referenceModelPart instanceof BasicValuedModelPart ) {
assSortSpecification(
(BasicValuedModelPart) referenceModelPart,
ast,
tableGroup,
collation,
sortOrder,
creationState
);
}
else if ( referenceModelPart instanceof EntityValuedModelPart ) {
final ModelPart subPart;
if ( ELEMENT_TOKEN.equals( modelPartName ) ) {
subPart = ( (EntityValuedModelPart) referenceModelPart ).getEntityMappingType().getIdentifierMapping();
}
else {
subPart = ( (EntityValuedModelPart) referenceModelPart ).findSubPart( modelPartName );
}
apply(
subPart,
ast,
tableGroup,
collation,
modelPartName,
sortOrder,
creationState,
sessionFactory,
sqlExprResolver
);
}
else if ( referenceModelPart instanceof EmbeddableValuedModelPart ) {
assSortSpecification(
(EmbeddableValuedModelPart) referenceModelPart,
ast,
tableGroup,
collation,
modelPartName,
sortOrder,
creationState,
sessionFactory,
sqlExprResolver
);
}
else {
throw new NotYetImplementedFor6Exception( "Ordering for " + getReferenceModelPart() + "not yet supported" );
}
}
private void assSortSpecification(
EmbeddableValuedModelPart embeddableValuedModelPart,
QuerySpec ast,
TableGroup tableGroup,
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState,
SessionFactoryImplementor sessionFactory,
SqlExpressionResolver sqlExprResolver) {
if ( embeddableValuedModelPart.getFetchableName()
.equals( modelPartName ) || ELEMENT_TOKEN.equals( modelPartName ) ) {
embeddableValuedModelPart.visitColumns(
(tableExpression, columnExpression, isColumnExpressionFormula, jdbcMapping) -> {
final TableReference tableReference = tableGroup.resolveTableReference( tableExpression );
ast.addSortSpecification(
new SortSpecification(
sqlExprResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableExpression,
columnExpression
),
sqlAstProcessingState -> new ColumnReference(
tableReference,
columnExpression,
isColumnExpressionFormula,
jdbcMapping,
sessionFactory
)
),
collation,
sortOrder
)
);
}
);
}
else {
ModelPart subPart = embeddableValuedModelPart.findSubPart( modelPartName, null );
assert subPart instanceof BasicValuedModelPart;
assSortSpecification(
(BasicValuedModelPart) subPart,
ast,
tableGroup,
collation,
sortOrder,
creationState
);
}
}
private void assSortSpecification(
BasicValuedModelPart basicValuedPart,
QuerySpec ast,
TableGroup tableGroup,
String collation,
SortOrder sortOrder,
SqlAstCreationState creationState) {
final TableReference tableReference = tableGroup.resolveTableReference( basicValuedPart.getContainingTableExpression() );
ast.addSortSpecification(
new SortSpecification(
new ColumnReference(
tableReference,
basicValuedPart.getMappedColumnExpression(),
basicValuedPart.isMappedColumnExpressionFormula(),
basicValuedPart.getJdbcMapping(),
creationState.getCreationContext().getSessionFactory()
),
collation,
sortOrder
)
);
}
}

View File

@ -36,6 +36,7 @@ public class OrderByFragmentImpl implements OrderByFragment {
ast,
tableGroup,
orderingSpec.getCollation(),
orderingSpec.getOrderByValue(),
orderingSpec.getSortOrder(),
creationState
);

View File

@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping.ordering.ast;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.AbstractDomainPath;
import org.hibernate.metamodel.mapping.internal.EmbeddedCollectionPart;
import org.hibernate.metamodel.mapping.ordering.TranslationContext;
import org.hibernate.query.NavigablePath;
@ -18,7 +19,7 @@ import org.hibernate.query.NavigablePath;
*
* @author Steve Ebersole
*/
public class CollectionPartPath implements DomainPath {
public class CollectionPartPath extends AbstractDomainPath {
private final NavigablePath navigablePath;
private final PluralAttributePath lhs;
private final CollectionPart referencedPart;

View File

@ -62,6 +62,7 @@ public class ColumnReference implements OrderingExpression, SequencePart {
QuerySpec ast,
TableGroup tableGroup,
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState) {
TableReference tableReference;

View File

@ -6,20 +6,9 @@
*/
package org.hibernate.metamodel.mapping.ordering.ast;
import org.hibernate.SortOrder;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SortSpecification;
/**
* Represents a domain-path (model part path) used in an order-by fragment
@ -36,63 +25,4 @@ public interface DomainPath extends OrderingExpression, SequencePart {
default PluralAttributeMapping getPluralAttribute() {
return getLhs().getPluralAttribute();
}
@Override
default void apply(
QuerySpec ast,
TableGroup tableGroup,
String collation,
SortOrder sortOrder,
SqlAstCreationState creationState) {
final SqlAstCreationContext creationContext = creationState.getCreationContext();
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
final SqlExpressionResolver sqlExprResolver = creationState.getSqlExpressionResolver();
if ( getReferenceModelPart() instanceof BasicValuedModelPart ) {
final BasicValuedModelPart basicValuedPart = (BasicValuedModelPart) getReferenceModelPart();
final TableReference tableReference = tableGroup.resolveTableReference( basicValuedPart.getContainingTableExpression() );
ast.addSortSpecification(
new SortSpecification(
new ColumnReference(
tableReference,
basicValuedPart.getMappedColumnExpression(),
basicValuedPart.isMappedColumnExpressionFormula(),
basicValuedPart.getJdbcMapping(),
creationState.getCreationContext().getSessionFactory()
),
collation,
sortOrder
)
);
}
else {
getReferenceModelPart().visitColumns(
(tableExpression, columnExpression, isColumnExpressionFormula, jdbcMapping) -> {
final TableReference tableReference = tableGroup.resolveTableReference( tableExpression );
ast.addSortSpecification(
new SortSpecification(
sqlExprResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableExpression,
columnExpression
),
sqlAstProcessingState -> new ColumnReference(
tableReference,
columnExpression,
isColumnExpressionFormula,
jdbcMapping,
sessionFactory
)
),
collation,
sortOrder
)
);
}
);
}
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping.ordering.ast;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.AbstractDomainPath;
import org.hibernate.metamodel.mapping.ordering.TranslationContext;
import org.hibernate.query.NavigablePath;
@ -17,7 +18,7 @@ import org.hibernate.query.NavigablePath;
*
* @author Steve Ebersole
*/
public class DomainPathContinuation implements DomainPath {
public class DomainPathContinuation extends AbstractDomainPath {
private final NavigablePath navigablePath;
private final DomainPath lhs;
private final ModelPart referencedModelPart;

View File

@ -49,6 +49,7 @@ public class FunctionExpression implements OrderingExpression {
QuerySpec ast,
TableGroup tableGroup,
String collation,
String modelPartName,
SortOrder sortOrder,
SqlAstCreationState creationState) {
throw new NotYetImplementedFor6Exception( getClass() );

View File

@ -20,5 +20,5 @@ public interface OrderingExpression extends Node {
/**
* Apply the SQL AST sort-specifications associated with this ordering-expression
*/
void apply(QuerySpec ast, TableGroup tableGroup, String collation, SortOrder sortOrder, SqlAstCreationState creationState);
void apply(QuerySpec ast, TableGroup tableGroup, String collation, String modelPartName, SortOrder sortOrder, SqlAstCreationState creationState);
}

View File

@ -20,13 +20,19 @@ public class OrderingSpecification implements Node {
private String collation;
private SortOrder sortOrder;
private NullPrecedence nullPrecedence = NullPrecedence.NONE;
private String orderByValue;
public OrderingSpecification(OrderingExpression orderingExpression) {
this.orderingExpression = orderingExpression;
public String getOrderByValue() {
return orderByValue;
}
public OrderingSpecification(OrderingExpression orderingExpression, SortOrder sortOrder) {
public OrderingSpecification(OrderingExpression orderingExpression, String orderByValue) {
this.orderingExpression = orderingExpression;
this.orderByValue = orderByValue;
}
public OrderingSpecification(OrderingExpression orderingExpression, String orderByValue,SortOrder sortOrder) {
this(orderingExpression, orderByValue);
this.sortOrder = sortOrder;
}
@ -57,4 +63,8 @@ public class OrderingSpecification implements Node {
public void setNullPrecedence(NullPrecedence nullPrecedence) {
this.nullPrecedence = nullPrecedence;
}
public void setOrderByValue(String orderByValue) {
this.orderByValue = orderByValue;
}
}

View File

@ -70,7 +70,7 @@ public class ParseTreeVisitor extends OrderingParserBaseVisitor<Object> {
}
}
final OrderingSpecification result = new OrderingSpecification( orderingExpression );
final OrderingSpecification result = new OrderingSpecification( orderingExpression, parsedSpec.expression().getText());
if ( parsedSpec.collationSpecification() != null ) {
result.setCollation( parsedSpec.collationSpecification().identifier().getText() );

View File

@ -10,17 +10,17 @@ import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.AbstractDomainPath;
import org.hibernate.metamodel.mapping.ordering.TranslationContext;
import org.hibernate.query.NavigablePath;
/**
* Represents the collection as a DomainPath
*
* @see RootSequencePart
*
* @author Steve Ebersole
* @see RootSequencePart
*/
public class PluralAttributePath implements DomainPath {
public class PluralAttributePath extends AbstractDomainPath {
private final NavigablePath navigablePath;
private final PluralAttributeMapping pluralAttributeMapping;
@ -67,6 +67,9 @@ public class PluralAttributePath implements DomainPath {
pluralAttributeMapping.getElementDescriptor()
);
}
else {
return new DomainPathContinuation( navigablePath.append( name ), this, subPart );
}
}
// the above checks for explicit element or index descriptor references

View File

@ -27,13 +27,13 @@ public class Person {
private Long id;
private String name;
private Set<String> nickNamesAscendingNaturalSort = new HashSet<String>();
private Set<String> nickNamesDescendingNaturalSort = new HashSet<String>();
private Set<String> nickNamesAscendingNaturalSort = new HashSet<>();
private Set<String> nickNamesDescendingNaturalSort = new HashSet<>();
private Set<Address> addressesAscendingNaturalSort = new HashSet<Address>();
private Set<Address> addressesDescendingNaturalSort = new HashSet<Address>();
private Set<Address> addressesCityAscendingSort = new HashSet<Address>();
private Set<Address> addressesCityDescendingSort = new HashSet<Address>();
private Set<Address> addressesAscendingNaturalSort = new HashSet<>();
private Set<Address> addressesDescendingNaturalSort = new HashSet<>();
private Set<Address> addressesCityAscendingSort = new HashSet<>();
private Set<Address> addressesCityDescendingSort = new HashSet<>();
@Id

View File

@ -4,7 +4,7 @@
* 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.test.collection.ordered.joinedInheritence;
package org.hibernate.orm.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;

View File

@ -4,7 +4,7 @@
* 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.test.collection.ordered.joinedInheritence;
package org.hibernate.orm.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;

View File

@ -0,0 +1,74 @@
/*
* 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.collection.ordered.joinedInheritence;
import org.hibernate.internal.util.collections.CollectionHelper;
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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
* @author Brett Meyer
*/
@DomainModel(
annotatedClasses = {
Animal.class, Lion.class, Tiger.class, Zoo.class
}
)
@SessionFactory
public class OrderCollectionOfJoinedHierarchyTest {
@Test
public void testQuerySyntaxCheck(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.get( Zoo.class, 1L )
);
}
@Test
public void testOrdering(SessionFactoryScope scope) {
Zoo zoo = new Zoo();
Lion lion1 = new Lion();
lion1.setWeight( 2 );
Lion lion2 = new Lion();
lion2.setWeight( 1 );
zoo.getLions().add( lion1 );
zoo.getLions().add( lion2 );
zoo.getAnimalsById().add( lion1 );
zoo.getAnimalsById().add( lion2 );
Zoo zoo1 = scope.fromTransaction(
session -> {
session.persist( lion1 );
session.persist( lion2 );
session.persist( zoo );
session.getTransaction().commit();
session.clear();
session.beginTransaction();
Zoo z = session.get( Zoo.class, zoo.getId() );
z.getLions().size();
z.getTigers().size();
z.getAnimalsById().size();
return z;
}
);
assertNotNull( zoo1 );
assertTrue( CollectionHelper.isNotEmpty( zoo1.getLions() ) && zoo1.getLions().size() == 2 );
assertTrue( CollectionHelper.isNotEmpty( zoo1.getAnimalsById() ) && zoo1.getAnimalsById().size() == 2 );
assertEquals( zoo1.getLions().iterator().next().getId(), lion2.getId() );
assertEquals( zoo1.getAnimalsById().iterator().next().getId(), lion1.getId() );
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.collection.ordered.joinedInheritence;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
/**
* @author Steve Ebersole
*/
@Entity
public class Tiger extends Animal {
private int numberOfStripes;
private Details details;
public int getNumberOfStripes() {
return numberOfStripes;
}
public void setNumberOfStripes(int numberOfStripes) {
this.numberOfStripes = numberOfStripes;
}
public Details getDetails() {
return details;
}
public void setDetails(Details details) {
this.details = details;
}
@Embeddable
public static class Details {
private String name;
private String sex;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -4,10 +4,9 @@
* 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.test.collection.ordered.joinedInheritence;
package org.hibernate.orm.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
@ -26,9 +25,9 @@ public class Zoo {
private Long id;
private String name;
private String city;
private Set<Tiger> tigers = new HashSet<Tiger>();
private Set<Lion> lions = new HashSet<Lion>();
private Set<Animal> animals = new HashSet<Animal>();
private Set<Tiger> tigers = new HashSet<>();
private Set<Lion> lions = new HashSet<>();
private Set<Animal> animals = new HashSet<>();
@Id
@GeneratedValue( generator = "increment" )
@ -59,7 +58,7 @@ public class Zoo {
@OneToMany
@JoinColumn
@javax.persistence.OrderBy( "weight" )
@javax.persistence.OrderBy( "details" )
public Set<Tiger> getTigers() {
return tigers;
}

View File

@ -1,74 +0,0 @@
/*
* 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.test.collection.ordered.joinedInheritence;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.hibernate.Session;
import org.junit.Test;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
/**
* @author Steve Ebersole
* @author Brett Meyer
*/
public class OrderCollectionOfJoinedHierarchyTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Animal.class, Lion.class, Tiger.class, Zoo.class };
}
@Test
public void testQuerySyntaxCheck() {
Session session = openSession();
session.beginTransaction();
session.get( Zoo.class, 1L );
session.getTransaction().commit();
session.close();
}
@Test
public void testOrdering() {
Zoo zoo = new Zoo();
Lion lion1 = new Lion();
lion1.setWeight( 2 );
Lion lion2 = new Lion();
lion2.setWeight( 1 );
zoo.getLions().add( lion1 );
zoo.getLions().add( lion2 );
zoo.getAnimalsById().add( lion1 );
zoo.getAnimalsById().add( lion2 );
Session session = openSession();
session.beginTransaction();
session.persist( lion1 );
session.persist( lion2 );
session.persist( zoo );
session.getTransaction().commit();
session.clear();
session.beginTransaction();
zoo = (Zoo) session.get( Zoo.class, zoo.getId() );
zoo.getLions().size();
zoo.getLions().size();
zoo.getAnimalsById().size();
session.getTransaction().commit();
session.close();
assertNotNull( zoo );
assertTrue( CollectionHelper.isNotEmpty( zoo.getLions() ) && zoo.getLions().size() == 2 );
assertTrue( CollectionHelper.isNotEmpty( zoo.getAnimalsById() ) && zoo.getAnimalsById().size() == 2 );
assertEquals( zoo.getLions().iterator().next().getId(), lion2.getId() );
assertEquals( zoo.getAnimalsById().iterator().next().getId(), lion1.getId() );
}
}

View File

@ -1,25 +0,0 @@
/*
* 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.test.collection.ordered.joinedInheritence;
import javax.persistence.Entity;
/**
* @author Steve Ebersole
*/
@Entity
public class Tiger extends Animal {
private int numberOfStripes;
public int getNumberOfStripes() {
return numberOfStripes;
}
public void setNumberOfStripes(int numberOfStripes) {
this.numberOfStripes = numberOfStripes;
}
}