From 942d1cb423593b297e27c3a222880313c5e7eed2 Mon Sep 17 00:00:00 2001 From: Kareem Elzayat Date: Fri, 8 Mar 2024 16:52:45 +0100 Subject: [PATCH] fix the getOrders() method (#2829) Co-authored-by: Kareem Elzayat --- .../AbstractHibernateOperations.java | 12 +++--- .../operations/HibernateJpaOperations.java | 1 - .../AbstractSpecificationInterceptor.java | 11 ++++- .../kotlin/example/ChildSuspendRepository.kt | 8 ++++ .../test/kotlin/example/HibernateTxTest.kt | 40 +++++++++++++++++++ 5 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 doc-examples/hibernate-example-kotlin/src/main/kotlin/example/ChildSuspendRepository.kt diff --git a/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/AbstractHibernateOperations.java b/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/AbstractHibernateOperations.java index d9fdac74af..0eef2aa998 100644 --- a/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/AbstractHibernateOperations.java +++ b/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/AbstractHibernateOperations.java @@ -620,14 +620,14 @@ protected final void collectCountOf(CriteriaBuilder criteriaBuilder, S sessi private void bindCriteriaSort(CriteriaQuery criteriaQuery, Root root, CriteriaBuilder builder, @NonNull Sort sort) { List orders = new ArrayList<>(); + Path path = null; for (Sort.Order order : sort.getOrderBy()) { - Path path = root.get(order.getProperty()); - Expression expression = order.isIgnoreCase() ? builder.lower(path) : path; - if (order.getDirection() == Sort.Order.Direction.DESC) { - orders.add(builder.desc(expression)); - } else { - orders.add(builder.asc(expression)); + String[] propertyArray = order.getProperty().split("\\."); + for (String property : propertyArray) { + path = path == null ? root.get(property) : path.get(property); } + Expression expression = order.isIgnoreCase() && path != null ? builder.lower(path.type().as(String.class)) : path; + orders.add(order.isAscending() ? builder.asc(expression) : builder.desc(expression)); } criteriaQuery.orderBy(orders); } diff --git a/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/HibernateJpaOperations.java b/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/HibernateJpaOperations.java index cbb0fb82f9..053088f462 100644 --- a/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/HibernateJpaOperations.java +++ b/data-hibernate-jpa/src/main/java/io/micronaut/data/hibernate/operations/HibernateJpaOperations.java @@ -50,7 +50,6 @@ import io.micronaut.data.runtime.convert.DataConversionService; import io.micronaut.data.runtime.operations.ExecutorAsyncOperations; import io.micronaut.data.runtime.operations.ExecutorAsyncOperationsSupportingCriteria; -import io.micronaut.data.runtime.operations.ExecutorReactiveOperations; import io.micronaut.data.runtime.operations.ExecutorReactiveOperationsSupportingCriteria; import io.micronaut.transaction.TransactionOperations; import jakarta.inject.Named; 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 c9366518f5..04fecee188 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 @@ -56,12 +56,14 @@ import jakarta.persistence.criteria.CriteriaDelete; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Order; import jakarta.persistence.criteria.Path; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -555,9 +557,14 @@ protected CriteriaUpdateBuilder getCriteriaUpdateBuilder(MethodInvocation private List getOrders(Sort sort, Root root, CriteriaBuilder cb) { List orders = new ArrayList<>(); + Path path = null; for (Sort.Order order : sort.getOrderBy()) { - Path propertyPath = root.get(order.getProperty()); - orders.add(order.isAscending() ? cb.asc(propertyPath) : cb.desc(propertyPath)); + String[] propertyArray = order.getProperty().split("\\."); + for (String property : propertyArray) { + path = path == null ? root.get(property) : path.get(property); + } + Expression expression = order.isIgnoreCase() && path != null ? cb.lower(path.type().as(String.class)) : path; + orders.add(order.isAscending() ? cb.asc(expression) : cb.desc(expression)); } return orders; } diff --git a/doc-examples/hibernate-example-kotlin/src/main/kotlin/example/ChildSuspendRepository.kt b/doc-examples/hibernate-example-kotlin/src/main/kotlin/example/ChildSuspendRepository.kt new file mode 100644 index 0000000000..f8f5ea36b2 --- /dev/null +++ b/doc-examples/hibernate-example-kotlin/src/main/kotlin/example/ChildSuspendRepository.kt @@ -0,0 +1,8 @@ +package example + +import io.micronaut.data.annotation.Repository +import io.micronaut.data.repository.CrudRepository +import io.micronaut.data.repository.jpa.kotlin.CoroutineJpaSpecificationExecutor + +@Repository +interface ChildSuspendRepository : CrudRepository, CoroutineJpaSpecificationExecutor diff --git a/doc-examples/hibernate-example-kotlin/src/test/kotlin/example/HibernateTxTest.kt b/doc-examples/hibernate-example-kotlin/src/test/kotlin/example/HibernateTxTest.kt index e080de9306..dd7db2cc7b 100644 --- a/doc-examples/hibernate-example-kotlin/src/test/kotlin/example/HibernateTxTest.kt +++ b/doc-examples/hibernate-example-kotlin/src/test/kotlin/example/HibernateTxTest.kt @@ -6,15 +6,19 @@ import io.micronaut.data.repository.jpa.criteria.PredicateSpecification import io.micronaut.data.repository.jpa.criteria.QuerySpecification import io.micronaut.data.runtime.criteria.get import io.micronaut.data.runtime.criteria.joinMany +import io.micronaut.data.runtime.criteria.joinOne import io.micronaut.data.runtime.criteria.query import io.micronaut.test.extensions.junit5.annotation.MicronautTest import jakarta.inject.Inject +import jakarta.persistence.criteria.Join import jakarta.persistence.criteria.JoinType import jakarta.persistence.criteria.ListJoin import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.hibernate.query.sqm.tree.domain.SqmListJoin +import org.hibernate.query.sqm.tree.domain.SqmSingularJoin import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.MethodOrderer @@ -37,9 +41,12 @@ class HibernateTxTest { private lateinit var repositoryForCustomDb: ParentRepositoryForCustomDb @Inject private lateinit var service: PersonSuspendRepositoryService + @Inject + private lateinit var childRepository: ChildSuspendRepository @BeforeEach fun cleanup() { + childRepository.deleteAll() repository.deleteAll() repositoryForCustomDb.deleteAll() } @@ -284,4 +291,37 @@ class HibernateTxTest { } } + @Test + fun `coroutineCriteria failing`() { + runBlocking { + val parent1 = Parent("abc", mutableListOf()).apply { children.add(Child("cde", parent = this)) } + val parent2 = Parent("cde", mutableListOf()).apply { children.add(Child("abc", parent = this)) } + repositorySuspended.save(parent1) + repositorySuspended.save(parent2) + + val query = query { + val parent: Join = if (query.resultType.kotlin != Long::class) { + root.fetch("parent") as SqmSingularJoin + } else { + root.joinOne(Child::parent) + } + + where { + or { + root[Child::name] inList listOf("abc", "cde") + parent[Parent::name] inList listOf("abc", "cde") + } + } + } + + val resultAsc = childRepository.findAll(query, Sort.of(Sort.Order("parent.name", Sort.Order.Direction.ASC, false))).toList() + assertEquals(parent1.name, resultAsc.first().parent?.name) + assertEquals(parent2.name, resultAsc.last().parent?.name) + + val resultDesc = childRepository.findAll(query, Sort.of(Sort.Order("parent.name", Sort.Order.Direction.DESC, false))).toList() + assertEquals(parent2.name, resultDesc.first().parent?.name) + assertEquals(parent1.name, resultDesc.last().parent?.name) + } + } + }