Skip to content

Commit

Permalink
Merge pull request #3307 from micronaut-projects/eorr
Browse files Browse the repository at this point in the history
Correct multi property order path
  • Loading branch information
dstepanov authored Jan 30, 2025
2 parents 8561918 + 8632e96 commit 7a0ce6d
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"))
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Order> predicate = null
mainEntityRepository.findAllByCriteria(predicate, pageable).totalPages == 1
then:
e.id
e.assoc.size() == 2
Expand Down Expand Up @@ -113,12 +125,18 @@ class EmbeddedAssociationJoinSpec extends Specification implements H2TestPropert
}

@JdbcRepository(dialect = Dialect.H2)
interface MainEntityRepository extends CrudRepository<MainEntity, Long> {
interface MainEntityRepository extends CrudRepository<MainEntity, Long>, JpaSpecificationExecutor<MainEntity> {

@Join(value = "assoc", type = Join.Type.FETCH)
@Join(value = "em.assoc", type = Join.Type.FETCH)
@Override
Optional<MainEntity> findById(Long aLong)

@JoinSpecifications(@Join(value = "child", type = Join.Type.LEFT_FETCH))
Page<MainEntity> findAll(Pageable pageable)

@JoinSpecifications(@Join(value = "child", type = Join.Type.LEFT_FETCH))
Page<MainEntity> findAllByCriteria(PredicateSpecification<Order> spec, Pageable pageable)
}

@JdbcRepository(dialect = Dialect.H2)
Expand Down Expand Up @@ -199,4 +217,4 @@ class MainEntityAssociation {
@GeneratedValue
Long id
String name
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<MyOrder, UUID> {
@NonNull
@JoinSpecifications({
@Join(value = "embedded", type = Join.Type.LEFT_FETCH)
})
Page<MyOrder> findAll(PredicateSpecification<MyOrder> spec, Pageable pageable);

}
Original file line number Diff line number Diff line change
@@ -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<MyOrder> predicate = null
when:
orderRepository.findAll(predicate, pageable)
then:
noExceptionThrown()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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()));
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<JoinPath> joinPaths = queryState.baseQueryDefinition().getJoinPaths();
List<JoinPath> newJoinPaths = new ArrayList<>(joinPaths.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Shipment, ShipmentId> {
List<Shipment> 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<Shipment, ShipmentId> {
List<Shipment> 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', """
Expand Down
Loading

0 comments on commit 7a0ce6d

Please sign in to comment.