From 8a671907b412b97e0b99ebf325fd262cec4c6786 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Wed, 29 Jan 2025 16:41:05 +0700 Subject: [PATCH 1/3] Correct multi property order path --- .../data/jdbc/h2/H2EmbeddedIdSpec.groovy | 30 +++++++++++++ .../EmbeddedAssociationJoinSpec.groovy | 22 +++++++++- .../processor/visitors/OrderBySpec.groovy | 42 +++++++++++++++++++ .../AbstractSpecificationInterceptor.java | 21 ++++++++-- 4 files changed, 110 insertions(+), 5 deletions(-) diff --git a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/H2EmbeddedIdSpec.groovy b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/H2EmbeddedIdSpec.groovy index 4a5151d3f05..935658f6652 100644 --- a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/H2EmbeddedIdSpec.groovy +++ b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/H2EmbeddedIdSpec.groovy @@ -18,6 +18,8 @@ package io.micronaut.data.jdbc.h2 import io.micronaut.core.annotation.Introspected import io.micronaut.data.annotation.* import io.micronaut.data.jdbc.annotation.JdbcRepository +import io.micronaut.data.model.Page +import io.micronaut.data.model.Pageable import io.micronaut.data.model.Sort import io.micronaut.data.model.query.builder.sql.Dialect import io.micronaut.data.repository.CrudRepository @@ -52,6 +54,9 @@ class H2EmbeddedIdSpec extends Specification { } void "test CRUD"() { + given: + repository.deleteAll() + when: ShipmentId id = new ShipmentId("a", "b") repository.save(new Shipment(id, "test")) @@ -156,6 +161,31 @@ class H2EmbeddedIdSpec extends Specification { then:"The entities where deleted" repository.count() == 0 } + + void "test criteria order of embedded"() { + given: + repository.deleteAll() + when: + ShipmentId id = new ShipmentId("a", "b") + repository.save(new Shipment(id, "test")) + + ShipmentId id2 = new ShipmentId("c", "d") + repository.save(new Shipment(id2, "test2")) + + ShipmentId id3 = new ShipmentId("e", "f") + repository.save(new Shipment(id3, "test3")) + + ShipmentId id4 = new ShipmentId("g", "h") + repository.save(new Shipment(id4, "test4")) + + Sort.Order.Direction sortDirection = Sort.Order.Direction.ASC; + Pageable pageable = Pageable.UNPAGED.order(new Sort.Order("shipmentId.city", sortDirection, false)); + def page = repository.findAll(pageable) + + then: + page.totalSize == 4 + page.content[0].shipmentId.city == "b" + } } @Entity diff --git a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/embeddedAssociation/EmbeddedAssociationJoinSpec.groovy b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/embeddedAssociation/EmbeddedAssociationJoinSpec.groovy index b2147cf7728..a2d1fe3a642 100644 --- a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/embeddedAssociation/EmbeddedAssociationJoinSpec.groovy +++ b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/embeddedAssociation/EmbeddedAssociationJoinSpec.groovy @@ -2,11 +2,18 @@ package io.micronaut.data.jdbc.h2.embeddedAssociation import io.micronaut.context.ApplicationContext import io.micronaut.data.annotation.* +import io.micronaut.data.annotation.repeatable.JoinSpecifications import io.micronaut.data.jdbc.annotation.JdbcRepository import io.micronaut.data.jdbc.h2.H2DBProperties import io.micronaut.data.jdbc.h2.H2TestPropertyProvider +import io.micronaut.data.model.Page +import io.micronaut.data.model.Pageable +import io.micronaut.data.model.Sort import io.micronaut.data.model.query.builder.sql.Dialect import io.micronaut.data.repository.CrudRepository +import io.micronaut.data.repository.jpa.JpaSpecificationExecutor +import io.micronaut.data.repository.jpa.criteria.PredicateSpecification +import io.micronaut.data.tck.entities.Order import io.micronaut.test.extensions.spock.annotation.MicronautTest import spock.lang.AutoCleanup import spock.lang.Shared @@ -64,6 +71,11 @@ class EmbeddedAssociationJoinSpec extends Specification implements H2TestPropert when: mainEntityRepository.save(e) e = mainEntityRepository.findById(e.id).get() + Sort.Order.Direction sortDirection = Sort.Order.Direction.ASC; + Pageable pageable = Pageable.UNPAGED.order(new Sort.Order("child.name", sortDirection, false)); + mainEntityRepository.findAll(pageable).totalPages == 1 + PredicateSpecification predicate = null + mainEntityRepository.findAllByCriteria(predicate, pageable).totalPages == 1 then: e.id e.assoc.size() == 2 @@ -113,12 +125,18 @@ class EmbeddedAssociationJoinSpec extends Specification implements H2TestPropert } @JdbcRepository(dialect = Dialect.H2) -interface MainEntityRepository extends CrudRepository { +interface MainEntityRepository extends CrudRepository, JpaSpecificationExecutor { @Join(value = "assoc", type = Join.Type.FETCH) @Join(value = "em.assoc", type = Join.Type.FETCH) @Override Optional findById(Long aLong) + + @JoinSpecifications(@Join(value = "child", type = Join.Type.LEFT_FETCH)) + Page findAll(Pageable pageable) + + @JoinSpecifications(@Join(value = "child", type = Join.Type.LEFT_FETCH)) + Page findAllByCriteria(PredicateSpecification spec, Pageable pageable) } @JdbcRepository(dialect = Dialect.H2) @@ -199,4 +217,4 @@ class MainEntityAssociation { @GeneratedValue Long id String name -} \ No newline at end of file +} diff --git a/data-processor/src/test/groovy/io/micronaut/data/processor/visitors/OrderBySpec.groovy b/data-processor/src/test/groovy/io/micronaut/data/processor/visitors/OrderBySpec.groovy index 49d772c35ac..334295830e6 100644 --- a/data-processor/src/test/groovy/io/micronaut/data/processor/visitors/OrderBySpec.groovy +++ b/data-processor/src/test/groovy/io/micronaut/data/processor/visitors/OrderBySpec.groovy @@ -18,8 +18,50 @@ package io.micronaut.data.processor.visitors import io.micronaut.data.annotation.Query import io.micronaut.data.tck.entities.Company +import static io.micronaut.data.processor.visitors.TestUtils.getQuery + class OrderBySpec extends AbstractDataSpec { + void "test sort embedded"() { + given: + def repository = buildRepository('test.TestRepository', ''' +import io.micronaut.data.annotation.Repository; +import io.micronaut.data.repository.GenericRepository; +import io.micronaut.data.tck.entities.Shipment; +import io.micronaut.data.tck.entities.ShipmentDto; +import io.micronaut.data.tck.entities.ShipmentId; +@Repository +interface TestRepository extends GenericRepository { + List findAllOrderByShipmentIdCity(); +} +''') + when: + def queryFindByText = getQuery(repository.getRequiredMethod("findAllOrderByShipmentIdCity")) + then: + queryFindByText == 'SELECT shipment_ FROM io.micronaut.data.tck.entities.Shipment AS shipment_ ORDER BY shipment_.shipmentId.city ASC' + } + + void "test sort embedded JDBC"() { + given: + def repository = buildRepository('test.TestRepository', ''' +import io.micronaut.data.jdbc.annotation.JdbcRepository; +import io.micronaut.data.model.query.builder.sql.Dialect; +import io.micronaut.data.repository.GenericRepository; +import io.micronaut.data.tck.entities.Shipment; +import io.micronaut.data.tck.entities.ShipmentDto; +import io.micronaut.data.tck.entities.ShipmentId; + +@JdbcRepository(dialect = Dialect.POSTGRES) +interface TestRepository extends GenericRepository { + List findAllOrderByShipmentIdCity(); +} +''') + when: + def queryFindByText = getQuery(repository.getRequiredMethod("findAllOrderByShipmentIdCity")) + then: + queryFindByText == 'SELECT shipment_."sp_country",shipment_."sp_city",shipment_."field" FROM "Shipment1" shipment_ ORDER BY shipment_."sp_city" ASC' + } + void "test order by date created"() { given: def repository = buildRepository('test.MyInterface', """ diff --git a/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java b/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java index 82b2b521081..1334f2e3d2c 100644 --- a/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java +++ b/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java @@ -62,6 +62,7 @@ import jakarta.persistence.criteria.Selection; import java.util.ArrayList; +import java.util.Iterator; import java.util.Collection; import java.util.Comparator; import java.util.List; @@ -376,7 +377,16 @@ private CriteriaQuery createSelectIdsCriteriaQuery(MethodInvocationCo selection.add(getIdExpression(root)); // We need to select all ordered properties from ORDER BY for DISTINCT to work properly for (Sort.Order order : sort.getOrderBy()) { - selection.add(root.get(order.getProperty())); + Path path = null; + for (Iterator iterator = StringUtils.splitOmitEmptyStrings(order.getProperty(), '.').iterator(); iterator.hasNext(); ) { + String next = iterator.next(); + if (iterator.hasNext()) { + path = root.join(next); + } else { + path = root.get(next); + } + } + selection.add(path); } criteriaQuery.multiselect(selection).distinct(true); if (specification != null) { @@ -577,8 +587,13 @@ private List getOrders(Sort sort, Root root, CriteriaBuilder cb) { List orders = new ArrayList<>(); for (Sort.Order order : sort.getOrderBy()) { Path path = root; - for (String property : StringUtils.splitOmitEmptyStrings(order.getProperty(), '.')) { - path = path.get(property); + for (Iterator iterator = StringUtils.splitOmitEmptyStrings(order.getProperty(), '.').iterator(); iterator.hasNext(); ) { + String next = iterator.next(); + if (iterator.hasNext()) { + path = root.join(next); + } else { + path = root.get(next); + } } Expression expression = order.isIgnoreCase() ? cb.lower(path.type().as(String.class)) : path; orders.add(order.isAscending() ? cb.asc(expression) : cb.desc(expression)); From a9568217f794a4d6eda469c4341acb1d0544aa9c Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Wed, 29 Jan 2025 17:03:11 +0700 Subject: [PATCH 2/3] Correct --- .../AbstractSpecificationInterceptor.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java b/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java index 1334f2e3d2c..c4c5140ad6d 100644 --- a/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java +++ b/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java @@ -55,6 +55,7 @@ import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.From; import jakarta.persistence.criteria.Order; import jakarta.persistence.criteria.Path; import jakarta.persistence.criteria.Predicate; @@ -377,13 +378,12 @@ private CriteriaQuery createSelectIdsCriteriaQuery(MethodInvocationCo selection.add(getIdExpression(root)); // We need to select all ordered properties from ORDER BY for DISTINCT to work properly for (Sort.Order order : sort.getOrderBy()) { - Path path = null; - for (Iterator iterator = StringUtils.splitOmitEmptyStrings(order.getProperty(), '.').iterator(); iterator.hasNext(); ) { - String next = iterator.next(); - if (iterator.hasNext()) { - path = root.join(next); + Path path = root; + for (String next : StringUtils.splitOmitEmptyStrings(order.getProperty(), '.')) { + if (path instanceof From from) { + path = from.join(next); } else { - path = root.get(next); + path = path.get(next); } } selection.add(path); @@ -587,12 +587,11 @@ private List getOrders(Sort sort, Root root, CriteriaBuilder cb) { List orders = new ArrayList<>(); for (Sort.Order order : sort.getOrderBy()) { Path path = root; - for (Iterator iterator = StringUtils.splitOmitEmptyStrings(order.getProperty(), '.').iterator(); iterator.hasNext(); ) { - String next = iterator.next(); - if (iterator.hasNext()) { - path = root.join(next); + for (String next : StringUtils.splitOmitEmptyStrings(order.getProperty(), '.')) { + if (path instanceof From from) { + path = from.join(next); } else { - path = root.get(next); + path = path.get(next); } } Expression expression = order.isIgnoreCase() ? cb.lower(path.type().as(String.class)) : path; From 8632e9666db355fe159c71e4fd3aa95941bf4be9 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Thu, 30 Jan 2025 13:22:58 +0700 Subject: [PATCH 3/3] Correct --- .../query/builder/CosmosSqlQueryBuilder2.java | 8 ++-- .../jdbc/h2/one2one/select/MyEmbedded.java | 33 +++++++++++++++++ .../data/jdbc/h2/one2one/select/MyOrder.java | 37 +++++++++++++++++++ .../h2/one2one/select/MyOrderRepository.java | 23 ++++++++++++ .../select/OneToOneProjectionSpec.groovy | 28 ++++++++++++++ .../data/model/PersistentPropertyPath.java | 15 ++++++++ .../sql/AbstractSqlLikeQueryBuilder2.java | 32 +++++++++++----- .../AbstractSpecificationInterceptor.java | 14 ++++--- 8 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyEmbedded.java create mode 100644 data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrder.java create mode 100644 data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrderRepository.java create mode 100644 data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/OneToOneProjectionSpec.groovy diff --git a/data-document-model/src/main/java/io/micronaut/data/document/model/query/builder/CosmosSqlQueryBuilder2.java b/data-document-model/src/main/java/io/micronaut/data/document/model/query/builder/CosmosSqlQueryBuilder2.java index 02450fbf2e8..afc26cd29b6 100644 --- a/data-document-model/src/main/java/io/micronaut/data/document/model/query/builder/CosmosSqlQueryBuilder2.java +++ b/data-document-model/src/main/java/io/micronaut/data/document/model/query/builder/CosmosSqlQueryBuilder2.java @@ -108,14 +108,14 @@ protected void appendPropertyProjection(QueryPropertyPath propertyPath) { } @Override - protected void appendAssociationProjection(Association association, PersistentPropertyPath propertyPath) { - String joinedPath = propertyPath.getPath(); + protected void appendAssociationProjection(PersistentAssociationPath associationPath) { + String joinedPath = associationPath.getPath(); if (!queryState.isJoined(joinedPath)) { query.setLength(query.length() - 1); return; } - String joinAlias = queryState.getJoinAlias(propertyPath.getPath()); - selectAllColumns(AnnotationMetadata.EMPTY_METADATA, association.getAssociatedEntity(), joinAlias); + String joinAlias = queryState.getJoinAlias(associationPath.getPath()); + selectAllColumns(AnnotationMetadata.EMPTY_METADATA, associationPath.getAssociation().getAssociatedEntity(), joinAlias); } @Override diff --git a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyEmbedded.java b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyEmbedded.java new file mode 100644 index 00000000000..4fe5ae730b6 --- /dev/null +++ b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyEmbedded.java @@ -0,0 +1,33 @@ +package io.micronaut.data.jdbc.h2.one2one.select; + +import io.micronaut.data.annotation.AutoPopulated; +import io.micronaut.data.annotation.Id; +import io.micronaut.data.annotation.MappedEntity; + +import java.util.UUID; + +@MappedEntity +public class MyEmbedded { + + @Id + @AutoPopulated + private UUID id; + + private String someProp; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getSomeProp() { + return someProp; + } + + public void setSomeProp(String someProp) { + this.someProp = someProp; + } +} diff --git a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrder.java b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrder.java new file mode 100644 index 00000000000..8b1fb97c018 --- /dev/null +++ b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrder.java @@ -0,0 +1,37 @@ +package io.micronaut.data.jdbc.h2.one2one.select; + +import io.micronaut.core.annotation.Nullable; +import io.micronaut.data.annotation.AutoPopulated; +import io.micronaut.data.annotation.Id; +import io.micronaut.data.annotation.MappedEntity; +import io.micronaut.data.annotation.Relation; + +import java.util.UUID; + +@MappedEntity +public class MyOrder { + + @Id + @AutoPopulated + private UUID orderId; + + @Nullable + @Relation(value = Relation.Kind.ONE_TO_ONE) + private MyEmbedded embedded; + + public UUID getOrderId() { + return orderId; + } + + public void setOrderId(UUID orderId) { + this.orderId = orderId; + } + + public MyEmbedded getEmbedded() { + return embedded; + } + + public void setEmbedded(MyEmbedded embedded) { + this.embedded = embedded; + } +} diff --git a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrderRepository.java b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrderRepository.java new file mode 100644 index 00000000000..86988b9d7e3 --- /dev/null +++ b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/MyOrderRepository.java @@ -0,0 +1,23 @@ +package io.micronaut.data.jdbc.h2.one2one.select; + +import io.micronaut.core.annotation.NonNull; +import io.micronaut.data.annotation.Join; +import io.micronaut.data.annotation.repeatable.JoinSpecifications; +import io.micronaut.data.jdbc.annotation.JdbcRepository; +import io.micronaut.data.model.Page; +import io.micronaut.data.model.Pageable; +import io.micronaut.data.model.query.builder.sql.Dialect; +import io.micronaut.data.repository.CrudRepository; +import io.micronaut.data.repository.jpa.criteria.PredicateSpecification; + +import java.util.UUID; + +@JdbcRepository(dialect = Dialect.H2) +public interface MyOrderRepository extends CrudRepository { + @NonNull + @JoinSpecifications({ + @Join(value = "embedded", type = Join.Type.LEFT_FETCH) + }) + Page findAll(PredicateSpecification spec, Pageable pageable); + +} diff --git a/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/OneToOneProjectionSpec.groovy b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/OneToOneProjectionSpec.groovy new file mode 100644 index 00000000000..951f613c698 --- /dev/null +++ b/data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/one2one/select/OneToOneProjectionSpec.groovy @@ -0,0 +1,28 @@ +package io.micronaut.data.jdbc.h2.one2one.select + +import io.micronaut.data.jdbc.h2.H2DBProperties +import io.micronaut.data.model.Pageable +import io.micronaut.data.model.Sort +import io.micronaut.data.repository.jpa.criteria.PredicateSpecification +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import spock.lang.Specification + +@H2DBProperties +@MicronautTest(transactional = false) +class OneToOneProjectionSpec extends Specification { + + @Inject + MyOrderRepository orderRepository + + void findAll_withPageableSort_andSearch() { + given: + Sort.Order.Direction sortDirection = Sort.Order.Direction.ASC + Pageable pageable = Pageable.UNPAGED.order(new Sort.Order("embedded.someProp", sortDirection, false)) + PredicateSpecification predicate = null + when: + orderRepository.findAll(predicate, pageable) + then: + noExceptionThrown() + } +} diff --git a/data-model/src/main/java/io/micronaut/data/model/PersistentPropertyPath.java b/data-model/src/main/java/io/micronaut/data/model/PersistentPropertyPath.java index cc96036363b..02bb4a93a22 100644 --- a/data-model/src/main/java/io/micronaut/data/model/PersistentPropertyPath.java +++ b/data-model/src/main/java/io/micronaut/data/model/PersistentPropertyPath.java @@ -200,6 +200,21 @@ public String getPath() { return path; } + /** + * @return The associations path + */ + @NonNull + public String getAssociationsPath() { + if (associations.isEmpty()) { + return ""; + } + StringJoiner joiner = new StringJoiner("."); + for (Association association : associations) { + joiner.add(association.getName()); + } + return joiner.toString(); + } + /** * @return The array path */ diff --git a/data-model/src/main/java/io/micronaut/data/model/query/builder/sql/AbstractSqlLikeQueryBuilder2.java b/data-model/src/main/java/io/micronaut/data/model/query/builder/sql/AbstractSqlLikeQueryBuilder2.java index a935e73765a..5ae1da730e1 100644 --- a/data-model/src/main/java/io/micronaut/data/model/query/builder/sql/AbstractSqlLikeQueryBuilder2.java +++ b/data-model/src/main/java/io/micronaut/data/model/query/builder/sql/AbstractSqlLikeQueryBuilder2.java @@ -2512,6 +2512,8 @@ public void visit(io.micronaut.data.model.jpa.criteria.PersistentPropertyPath } else { query.setLength(query.length() - 1); } + } else if (!propertyPath.getAssociations().isEmpty() && queryState.isJoined(propertyPath.getAssociationsPath())) { + appendPropertyProjection(findProperty(propertyPath.getPath())); } else { appendCompoundPropertyProjection(propertyPath); } @@ -2520,7 +2522,7 @@ public void visit(io.micronaut.data.model.jpa.criteria.PersistentPropertyPath query.append(DISTINCT); } if (property instanceof Association association && !property.isEmbedded()) { - appendAssociationProjection(association, propertyPath); + appendAssociationProjection(new PersistentAssociationPath(propertyPath.getAssociations(), association)); } else { appendPropertyProjection(findProperty(propertyPath.getPath())); } @@ -2681,7 +2683,21 @@ protected void appendCompoundPropertyProjection(PersistentPropertyPath propertyP @Internal protected void appendCompoundAssociationProjection(PersistentAssociationPath propertyPath) { if (!query.isEmpty() && query.charAt(query.length() - 1) == ',') { - // Strip last . + // Strip last , + query.setLength(query.length() - 1); + } + selectAllColumnsFromJoinPaths(queryState.baseQueryDefinition().getJoinPaths(), null); + } + + /** + * Appends the compound (part of entity or DTO) association projection. + * + * @param propertyPath The property path + */ + @Internal + protected void appendCompoundProjection(PersistentPropertyPath propertyPath) { + if (!query.isEmpty() && query.charAt(query.length() - 1) == ',') { + // Strip last , query.setLength(query.length() - 1); } selectAllColumnsFromJoinPaths(queryState.baseQueryDefinition().getJoinPaths(), null); @@ -2737,19 +2753,17 @@ protected void appendPropertyProjection(QueryPropertyPath propertyPath) { /** * Appends selection projection for the property which is association. * - * @param association the persistent property - * @param propertyPath the persistent property path + * @param associationPath the persistent property path */ - protected void appendAssociationProjection(Association association, - PersistentPropertyPath propertyPath) { - String joinedPath = propertyPath.getPath(); + protected void appendAssociationProjection(PersistentAssociationPath associationPath) { + String joinedPath = associationPath.getPath(); if (!queryState.isJoined(joinedPath)) { query.setLength(query.length() - 1); return; } - String joinAlias = queryState.findJoinAlias(propertyPath.getPath()); + String joinAlias = queryState.findJoinAlias(associationPath.getPath()); - selectAllColumns(AnnotationMetadata.EMPTY_METADATA, association.getAssociatedEntity(), joinAlias); + selectAllColumns(AnnotationMetadata.EMPTY_METADATA, associationPath.getAssociation().getAssociatedEntity(), joinAlias); Collection joinPaths = queryState.baseQueryDefinition().getJoinPaths(); List newJoinPaths = new ArrayList<>(joinPaths.size()); diff --git a/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java b/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java index c4c5140ad6d..b90babcc425 100644 --- a/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java +++ b/data-runtime/src/main/java/io/micronaut/data/runtime/intercept/criteria/AbstractSpecificationInterceptor.java @@ -379,9 +379,10 @@ private CriteriaQuery createSelectIdsCriteriaQuery(MethodInvocationCo // We need to select all ordered properties from ORDER BY for DISTINCT to work properly for (Sort.Order order : sort.getOrderBy()) { Path path = root; - for (String next : StringUtils.splitOmitEmptyStrings(order.getProperty(), '.')) { - if (path instanceof From from) { - path = from.join(next); + for (Iterator iterator = StringUtils.splitOmitEmptyStrings(order.getProperty(), '.').iterator(); iterator.hasNext(); ) { + String next = iterator.next(); + if (iterator.hasNext()) { + path = ((From) path).join(next); } else { path = path.get(next); } @@ -587,9 +588,10 @@ private List getOrders(Sort sort, Root root, CriteriaBuilder cb) { List orders = new ArrayList<>(); for (Sort.Order order : sort.getOrderBy()) { Path path = root; - for (String next : StringUtils.splitOmitEmptyStrings(order.getProperty(), '.')) { - if (path instanceof From from) { - path = from.join(next); + for (Iterator iterator = StringUtils.splitOmitEmptyStrings(order.getProperty(), '.').iterator(); iterator.hasNext(); ) { + String next = iterator.next(); + if (iterator.hasNext()) { + path = ((From) path).join(next); } else { path = path.get(next); }