Skip to content

Commit 7a0ce6d

Browse files
authored
Merge pull request #3307 from micronaut-projects/eorr
Correct multi property order path
2 parents 8561918 + 8632e96 commit 7a0ce6d

File tree

11 files changed

+274
-18
lines changed

11 files changed

+274
-18
lines changed

data-document-model/src/main/java/io/micronaut/data/document/model/query/builder/CosmosSqlQueryBuilder2.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ protected void appendPropertyProjection(QueryPropertyPath propertyPath) {
108108
}
109109

110110
@Override
111-
protected void appendAssociationProjection(Association association, PersistentPropertyPath propertyPath) {
112-
String joinedPath = propertyPath.getPath();
111+
protected void appendAssociationProjection(PersistentAssociationPath associationPath) {
112+
String joinedPath = associationPath.getPath();
113113
if (!queryState.isJoined(joinedPath)) {
114114
query.setLength(query.length() - 1);
115115
return;
116116
}
117-
String joinAlias = queryState.getJoinAlias(propertyPath.getPath());
118-
selectAllColumns(AnnotationMetadata.EMPTY_METADATA, association.getAssociatedEntity(), joinAlias);
117+
String joinAlias = queryState.getJoinAlias(associationPath.getPath());
118+
selectAllColumns(AnnotationMetadata.EMPTY_METADATA, associationPath.getAssociation().getAssociatedEntity(), joinAlias);
119119
}
120120

121121
@Override

data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/H2EmbeddedIdSpec.groovy

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package io.micronaut.data.jdbc.h2
1818
import io.micronaut.core.annotation.Introspected
1919
import io.micronaut.data.annotation.*
2020
import io.micronaut.data.jdbc.annotation.JdbcRepository
21+
import io.micronaut.data.model.Page
22+
import io.micronaut.data.model.Pageable
2123
import io.micronaut.data.model.Sort
2224
import io.micronaut.data.model.query.builder.sql.Dialect
2325
import io.micronaut.data.repository.CrudRepository
@@ -52,6 +54,9 @@ class H2EmbeddedIdSpec extends Specification {
5254
}
5355

5456
void "test CRUD"() {
57+
given:
58+
repository.deleteAll()
59+
5560
when:
5661
ShipmentId id = new ShipmentId("a", "b")
5762
repository.save(new Shipment(id, "test"))
@@ -156,6 +161,31 @@ class H2EmbeddedIdSpec extends Specification {
156161
then:"The entities where deleted"
157162
repository.count() == 0
158163
}
164+
165+
void "test criteria order of embedded"() {
166+
given:
167+
repository.deleteAll()
168+
when:
169+
ShipmentId id = new ShipmentId("a", "b")
170+
repository.save(new Shipment(id, "test"))
171+
172+
ShipmentId id2 = new ShipmentId("c", "d")
173+
repository.save(new Shipment(id2, "test2"))
174+
175+
ShipmentId id3 = new ShipmentId("e", "f")
176+
repository.save(new Shipment(id3, "test3"))
177+
178+
ShipmentId id4 = new ShipmentId("g", "h")
179+
repository.save(new Shipment(id4, "test4"))
180+
181+
Sort.Order.Direction sortDirection = Sort.Order.Direction.ASC;
182+
Pageable pageable = Pageable.UNPAGED.order(new Sort.Order("shipmentId.city", sortDirection, false));
183+
def page = repository.findAll(pageable)
184+
185+
then:
186+
page.totalSize == 4
187+
page.content[0].shipmentId.city == "b"
188+
}
159189
}
160190

161191
@Entity

data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/embeddedAssociation/EmbeddedAssociationJoinSpec.groovy

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ package io.micronaut.data.jdbc.h2.embeddedAssociation
22

33
import io.micronaut.context.ApplicationContext
44
import io.micronaut.data.annotation.*
5+
import io.micronaut.data.annotation.repeatable.JoinSpecifications
56
import io.micronaut.data.jdbc.annotation.JdbcRepository
67
import io.micronaut.data.jdbc.h2.H2DBProperties
78
import io.micronaut.data.jdbc.h2.H2TestPropertyProvider
9+
import io.micronaut.data.model.Page
10+
import io.micronaut.data.model.Pageable
11+
import io.micronaut.data.model.Sort
812
import io.micronaut.data.model.query.builder.sql.Dialect
913
import io.micronaut.data.repository.CrudRepository
14+
import io.micronaut.data.repository.jpa.JpaSpecificationExecutor
15+
import io.micronaut.data.repository.jpa.criteria.PredicateSpecification
16+
import io.micronaut.data.tck.entities.Order
1017
import io.micronaut.test.extensions.spock.annotation.MicronautTest
1118
import spock.lang.AutoCleanup
1219
import spock.lang.Shared
@@ -64,6 +71,11 @@ class EmbeddedAssociationJoinSpec extends Specification implements H2TestPropert
6471
when:
6572
mainEntityRepository.save(e)
6673
e = mainEntityRepository.findById(e.id).get()
74+
Sort.Order.Direction sortDirection = Sort.Order.Direction.ASC;
75+
Pageable pageable = Pageable.UNPAGED.order(new Sort.Order("child.name", sortDirection, false));
76+
mainEntityRepository.findAll(pageable).totalPages == 1
77+
PredicateSpecification<Order> predicate = null
78+
mainEntityRepository.findAllByCriteria(predicate, pageable).totalPages == 1
6779
then:
6880
e.id
6981
e.assoc.size() == 2
@@ -113,12 +125,18 @@ class EmbeddedAssociationJoinSpec extends Specification implements H2TestPropert
113125
}
114126

115127
@JdbcRepository(dialect = Dialect.H2)
116-
interface MainEntityRepository extends CrudRepository<MainEntity, Long> {
128+
interface MainEntityRepository extends CrudRepository<MainEntity, Long>, JpaSpecificationExecutor<MainEntity> {
117129

118130
@Join(value = "assoc", type = Join.Type.FETCH)
119131
@Join(value = "em.assoc", type = Join.Type.FETCH)
120132
@Override
121133
Optional<MainEntity> findById(Long aLong)
134+
135+
@JoinSpecifications(@Join(value = "child", type = Join.Type.LEFT_FETCH))
136+
Page<MainEntity> findAll(Pageable pageable)
137+
138+
@JoinSpecifications(@Join(value = "child", type = Join.Type.LEFT_FETCH))
139+
Page<MainEntity> findAllByCriteria(PredicateSpecification<Order> spec, Pageable pageable)
122140
}
123141

124142
@JdbcRepository(dialect = Dialect.H2)
@@ -199,4 +217,4 @@ class MainEntityAssociation {
199217
@GeneratedValue
200218
Long id
201219
String name
202-
}
220+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.micronaut.data.jdbc.h2.one2one.select;
2+
3+
import io.micronaut.data.annotation.AutoPopulated;
4+
import io.micronaut.data.annotation.Id;
5+
import io.micronaut.data.annotation.MappedEntity;
6+
7+
import java.util.UUID;
8+
9+
@MappedEntity
10+
public class MyEmbedded {
11+
12+
@Id
13+
@AutoPopulated
14+
private UUID id;
15+
16+
private String someProp;
17+
18+
public UUID getId() {
19+
return id;
20+
}
21+
22+
public void setId(UUID id) {
23+
this.id = id;
24+
}
25+
26+
public String getSomeProp() {
27+
return someProp;
28+
}
29+
30+
public void setSomeProp(String someProp) {
31+
this.someProp = someProp;
32+
}
33+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.micronaut.data.jdbc.h2.one2one.select;
2+
3+
import io.micronaut.core.annotation.Nullable;
4+
import io.micronaut.data.annotation.AutoPopulated;
5+
import io.micronaut.data.annotation.Id;
6+
import io.micronaut.data.annotation.MappedEntity;
7+
import io.micronaut.data.annotation.Relation;
8+
9+
import java.util.UUID;
10+
11+
@MappedEntity
12+
public class MyOrder {
13+
14+
@Id
15+
@AutoPopulated
16+
private UUID orderId;
17+
18+
@Nullable
19+
@Relation(value = Relation.Kind.ONE_TO_ONE)
20+
private MyEmbedded embedded;
21+
22+
public UUID getOrderId() {
23+
return orderId;
24+
}
25+
26+
public void setOrderId(UUID orderId) {
27+
this.orderId = orderId;
28+
}
29+
30+
public MyEmbedded getEmbedded() {
31+
return embedded;
32+
}
33+
34+
public void setEmbedded(MyEmbedded embedded) {
35+
this.embedded = embedded;
36+
}
37+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.micronaut.data.jdbc.h2.one2one.select;
2+
3+
import io.micronaut.core.annotation.NonNull;
4+
import io.micronaut.data.annotation.Join;
5+
import io.micronaut.data.annotation.repeatable.JoinSpecifications;
6+
import io.micronaut.data.jdbc.annotation.JdbcRepository;
7+
import io.micronaut.data.model.Page;
8+
import io.micronaut.data.model.Pageable;
9+
import io.micronaut.data.model.query.builder.sql.Dialect;
10+
import io.micronaut.data.repository.CrudRepository;
11+
import io.micronaut.data.repository.jpa.criteria.PredicateSpecification;
12+
13+
import java.util.UUID;
14+
15+
@JdbcRepository(dialect = Dialect.H2)
16+
public interface MyOrderRepository extends CrudRepository<MyOrder, UUID> {
17+
@NonNull
18+
@JoinSpecifications({
19+
@Join(value = "embedded", type = Join.Type.LEFT_FETCH)
20+
})
21+
Page<MyOrder> findAll(PredicateSpecification<MyOrder> spec, Pageable pageable);
22+
23+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.micronaut.data.jdbc.h2.one2one.select
2+
3+
import io.micronaut.data.jdbc.h2.H2DBProperties
4+
import io.micronaut.data.model.Pageable
5+
import io.micronaut.data.model.Sort
6+
import io.micronaut.data.repository.jpa.criteria.PredicateSpecification
7+
import io.micronaut.test.extensions.spock.annotation.MicronautTest
8+
import jakarta.inject.Inject
9+
import spock.lang.Specification
10+
11+
@H2DBProperties
12+
@MicronautTest(transactional = false)
13+
class OneToOneProjectionSpec extends Specification {
14+
15+
@Inject
16+
MyOrderRepository orderRepository
17+
18+
void findAll_withPageableSort_andSearch() {
19+
given:
20+
Sort.Order.Direction sortDirection = Sort.Order.Direction.ASC
21+
Pageable pageable = Pageable.UNPAGED.order(new Sort.Order("embedded.someProp", sortDirection, false))
22+
PredicateSpecification<MyOrder> predicate = null
23+
when:
24+
orderRepository.findAll(predicate, pageable)
25+
then:
26+
noExceptionThrown()
27+
}
28+
}

data-model/src/main/java/io/micronaut/data/model/PersistentPropertyPath.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,21 @@ public String getPath() {
200200
return path;
201201
}
202202

203+
/**
204+
* @return The associations path
205+
*/
206+
@NonNull
207+
public String getAssociationsPath() {
208+
if (associations.isEmpty()) {
209+
return "";
210+
}
211+
StringJoiner joiner = new StringJoiner(".");
212+
for (Association association : associations) {
213+
joiner.add(association.getName());
214+
}
215+
return joiner.toString();
216+
}
217+
203218
/**
204219
* @return The array path
205220
*/

data-model/src/main/java/io/micronaut/data/model/query/builder/sql/AbstractSqlLikeQueryBuilder2.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,6 +2512,8 @@ public void visit(io.micronaut.data.model.jpa.criteria.PersistentPropertyPath<?>
25122512
} else {
25132513
query.setLength(query.length() - 1);
25142514
}
2515+
} else if (!propertyPath.getAssociations().isEmpty() && queryState.isJoined(propertyPath.getAssociationsPath())) {
2516+
appendPropertyProjection(findProperty(propertyPath.getPath()));
25152517
} else {
25162518
appendCompoundPropertyProjection(propertyPath);
25172519
}
@@ -2520,7 +2522,7 @@ public void visit(io.micronaut.data.model.jpa.criteria.PersistentPropertyPath<?>
25202522
query.append(DISTINCT);
25212523
}
25222524
if (property instanceof Association association && !property.isEmbedded()) {
2523-
appendAssociationProjection(association, propertyPath);
2525+
appendAssociationProjection(new PersistentAssociationPath(propertyPath.getAssociations(), association));
25242526
} else {
25252527
appendPropertyProjection(findProperty(propertyPath.getPath()));
25262528
}
@@ -2681,7 +2683,21 @@ protected void appendCompoundPropertyProjection(PersistentPropertyPath propertyP
26812683
@Internal
26822684
protected void appendCompoundAssociationProjection(PersistentAssociationPath propertyPath) {
26832685
if (!query.isEmpty() && query.charAt(query.length() - 1) == ',') {
2684-
// Strip last .
2686+
// Strip last ,
2687+
query.setLength(query.length() - 1);
2688+
}
2689+
selectAllColumnsFromJoinPaths(queryState.baseQueryDefinition().getJoinPaths(), null);
2690+
}
2691+
2692+
/**
2693+
* Appends the compound (part of entity or DTO) association projection.
2694+
*
2695+
* @param propertyPath The property path
2696+
*/
2697+
@Internal
2698+
protected void appendCompoundProjection(PersistentPropertyPath propertyPath) {
2699+
if (!query.isEmpty() && query.charAt(query.length() - 1) == ',') {
2700+
// Strip last ,
26852701
query.setLength(query.length() - 1);
26862702
}
26872703
selectAllColumnsFromJoinPaths(queryState.baseQueryDefinition().getJoinPaths(), null);
@@ -2737,19 +2753,17 @@ protected void appendPropertyProjection(QueryPropertyPath propertyPath) {
27372753
/**
27382754
* Appends selection projection for the property which is association.
27392755
*
2740-
* @param association the persistent property
2741-
* @param propertyPath the persistent property path
2756+
* @param associationPath the persistent property path
27422757
*/
2743-
protected void appendAssociationProjection(Association association,
2744-
PersistentPropertyPath propertyPath) {
2745-
String joinedPath = propertyPath.getPath();
2758+
protected void appendAssociationProjection(PersistentAssociationPath associationPath) {
2759+
String joinedPath = associationPath.getPath();
27462760
if (!queryState.isJoined(joinedPath)) {
27472761
query.setLength(query.length() - 1);
27482762
return;
27492763
}
2750-
String joinAlias = queryState.findJoinAlias(propertyPath.getPath());
2764+
String joinAlias = queryState.findJoinAlias(associationPath.getPath());
27512765

2752-
selectAllColumns(AnnotationMetadata.EMPTY_METADATA, association.getAssociatedEntity(), joinAlias);
2766+
selectAllColumns(AnnotationMetadata.EMPTY_METADATA, associationPath.getAssociation().getAssociatedEntity(), joinAlias);
27532767

27542768
Collection<JoinPath> joinPaths = queryState.baseQueryDefinition().getJoinPaths();
27552769
List<JoinPath> newJoinPaths = new ArrayList<>(joinPaths.size());

data-processor/src/test/groovy/io/micronaut/data/processor/visitors/OrderBySpec.groovy

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,50 @@ package io.micronaut.data.processor.visitors
1818
import io.micronaut.data.annotation.Query
1919
import io.micronaut.data.tck.entities.Company
2020

21+
import static io.micronaut.data.processor.visitors.TestUtils.getQuery
22+
2123
class OrderBySpec extends AbstractDataSpec {
2224

25+
void "test sort embedded"() {
26+
given:
27+
def repository = buildRepository('test.TestRepository', '''
28+
import io.micronaut.data.annotation.Repository;
29+
import io.micronaut.data.repository.GenericRepository;
30+
import io.micronaut.data.tck.entities.Shipment;
31+
import io.micronaut.data.tck.entities.ShipmentDto;
32+
import io.micronaut.data.tck.entities.ShipmentId;
33+
@Repository
34+
interface TestRepository extends GenericRepository<Shipment, ShipmentId> {
35+
List<Shipment> findAllOrderByShipmentIdCity();
36+
}
37+
''')
38+
when:
39+
def queryFindByText = getQuery(repository.getRequiredMethod("findAllOrderByShipmentIdCity"))
40+
then:
41+
queryFindByText == 'SELECT shipment_ FROM io.micronaut.data.tck.entities.Shipment AS shipment_ ORDER BY shipment_.shipmentId.city ASC'
42+
}
43+
44+
void "test sort embedded JDBC"() {
45+
given:
46+
def repository = buildRepository('test.TestRepository', '''
47+
import io.micronaut.data.jdbc.annotation.JdbcRepository;
48+
import io.micronaut.data.model.query.builder.sql.Dialect;
49+
import io.micronaut.data.repository.GenericRepository;
50+
import io.micronaut.data.tck.entities.Shipment;
51+
import io.micronaut.data.tck.entities.ShipmentDto;
52+
import io.micronaut.data.tck.entities.ShipmentId;
53+
54+
@JdbcRepository(dialect = Dialect.POSTGRES)
55+
interface TestRepository extends GenericRepository<Shipment, ShipmentId> {
56+
List<Shipment> findAllOrderByShipmentIdCity();
57+
}
58+
''')
59+
when:
60+
def queryFindByText = getQuery(repository.getRequiredMethod("findAllOrderByShipmentIdCity"))
61+
then:
62+
queryFindByText == 'SELECT shipment_."sp_country",shipment_."sp_city",shipment_."field" FROM "Shipment1" shipment_ ORDER BY shipment_."sp_city" ASC'
63+
}
64+
2365
void "test order by date created"() {
2466
given:
2567
def repository = buildRepository('test.MyInterface', """

0 commit comments

Comments
 (0)