Blazebit / blaze-persistence

Rich Criteria API for JPA providers
https://persistence.blazebit.com
Apache License 2.0
727 stars 85 forks source link

Inlining count query does not work with Hibernate Subselect Fetch #993

Closed jwgmeligmeyling closed 4 years ago

jwgmeligmeyling commented 4 years ago

Description

Inlining count query does not work with Hibernate Subselect Fetch.

This is because the inlined query introduces parameters which cannot be rendered on the used subselect fetch query fragment.

This bug was introduced with the inlining of the count query in 1.4.x and is specific to Hibernate in domains where results from paginated CTE queries use Hibernates proprietary SUBSELECT fetch mechanism to load plural associations.

Expected behavior

Entities using subselect fetch in a paginated query result are able to load

Actual behavior

It fails with a parameter binding exception

Steps to reproduce

All required is a paginated query that uses a CTE and has a parameter or non-literal constant in i.e. the WHERE clause of the main query.


@Test
// NOTE: MySQL has no CTE support
// TODO: Oracle requires a cycle clause #295
@Category({ NoMySQL.class, NoOracle.class })
public void testFetchModeSubselectOnPaginatedCteQueryResult() throws Exception {
    CriteriaBuilder<RecursiveEntityCte> cb = cbf.create(em, RecursiveEntityCte.class)
        .withRecursive(RecursiveEntityCte.class)
            .from(RecursiveEntity.class, "recEntity")
            .bind("id").select("recEntity.id")
            .bind("name").select("recEntity.name")
            .bind("parent").select("recEntity.parent")
            .where("recEntity.name").notEq("root1")
            .where("recEntity.parent").isNotNull()
        .unionAll()
            .from(RecursiveEntity.class, "recEntity")
            .from(RecursiveEntityCte.class, "parentRecEntity")
            .bind("id").select("recEntity.id")
            .bind("name").select("recEntity.name")
            .bind("parent").select("recEntity.parent")
            .where("recEntity.id").eqExpression("parentRecEntity.parent.id")
        .end()
        .fetch("parent")
        .where("id").le(1000L)
        .orderByAsc("name")
        .orderByAsc("id");

    List<RecursiveEntityCte> result = cb.page(0, 10).getResultList();
    RecursiveEntity firstParent = result.get(0).getParent();
    Assert.assertEquals(2, firstParent.getChildren().size());
    Assert.assertEquals(1, firstParent.getManyToManyChildren().size());
}

Environment

Version: 1.4.1.Final (presumable 1.4.0.Final as well) JPA-Provider: Hibernate 5.4.x (Hibernate specific) DBMS: H2 and PostgreSQL Application Server: WildFly

jwgmeligmeyling commented 4 years ago

I pushed a reproducer to https://github.com/Blazebit/blaze-persistence/compare/master...jwgmeligmeyling:issues/993