Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct multi property order path #3307

Merged
merged 3 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading