micronaut-projects/micronaut-data

Micronaut Data JPA Specifications with Projections

Closed this issue · 4 comments

Issue description

I am trying to use Specification with Projection.

Following is the projection :

@Introspected
public class ProductShortDto {

    private Long id;

    private String name;
}

My repository method is:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<ProductShortDto> {

}

My Query specifications builder:

public class ProductShortSpecifications {

    public static QuerySpecification<ProductShortDto> name(String name) {
        return (root, query, cb) -> name == null ? null :
                cb.equal(root.get("name"), name);
    }
}

Micronaut Data version: 4.9.2

I tried invoking the repository using findAll:

        List<ProductShortDto> all = productRepository.findAll(productFilters.toShortPredicate());

But the select query has all the columns. How do I select only the columns mentioned in projection and its distinct values?

The generic of JpaSpecificationExecutor is supposed to be the entity root.

Use CriteriaQueryBuilder for this scenario:

      var query = cb.createQuery(ProductShortDto)
      var root = query.from(Product)

      query.where(cb.equal(root.get("name"), name))
      return query

Note this will make Hibernate create a DTO, not Micronaut.

@dstepanov thanks for reply.

I am trying to use this solution, but it’s not working for me. Do you have any example of how to implement it?

I have modified the repository in the following way:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
    List<ProductShortDto> getAll(QuerySpecification<ProductShortDto> querySpecification);
}

And I have this method to return a QuerySpecification:

    public static QuerySpecification<ProductShortDto> name(String name) {
        return (root, query, cb) -> {
            CriteriaQuery<ProductShortDto> query1 = cb.createQuery(ProductShortDto.class);
            Root<Product> root1 = query1.from(Product.class);
            query1.where(cb.equal(root1.get("name"), name));
            return query1.getRestriction();
        };
    }

When fetching data, I get the following error:

org.hibernate.sql.ast.SqlTreeCreationException: Could not locate TableGroup - dev.itrust.product.Product(799810928193541)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.prepareReusablePath(BaseSqmToSqlAstConverter.java:3703)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.prepareReusablePath(BaseSqmToSqlAstConverter.java:3646)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.prepareReusablePath(BaseSqmToSqlAstConverter.java:3630)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitBasicValuedPath(BaseSqmToSqlAstConverter.java:4298)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitBasicValuedPath(BaseSqmToSqlAstConverter.java:445)
	at org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath.accept(SqmBasicValuedSimplePath.java:145)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:7549)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitComparisonPredicate(BaseSqmToSqlAstConverter.java:445)
	at org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate.accept(SqmComparisonPredicate.java:111)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitWhereClause(BaseSqmToSqlAstConverter.java:2484)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:2080)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:445)
	at org.hibernate.query.sqm.tree.select.SqmQuerySpec.accept(SqmQuerySpec.java:122)
	at org.hibernate.query.sqm.spi.BaseSemanticQueryWalker.visitQueryPart(BaseSemanticQueryWalker.java:244)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQueryPart(BaseSqmToSqlAstConverter.java:1934)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:1619)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:445)
	at org.hibernate.query.sqm.tree.select.SqmSelectStatement.accept(SqmSelectStatement.java:234)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.translate(BaseSqmToSqlAstConverter.java:784)
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.buildCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:422)
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:328)
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:302)
	at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:526)
	at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:423)
	at org.hibernate.query.Query.getResultList(Query.java:120)

Please use CriteriaQueryBuilder

I will close it as it's not a bug.