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

Entity view inheritance and FetchType.SUBSELECT with @EmbeddedId throws UnsuportedOperationException #1945

Open
retroandchill opened this issue Oct 25, 2024 · 7 comments
Assignees
Labels
component: entity-view kind: bug worth: high Implementing this has a high worth
Milestone

Comments

@retroandchill
Copy link

Description

I'm currently trying to translate a fairly deep inheritance hierarchy of entities to use Blaze persistence and I'm running into some issues with the query of the view when using Hibernate.

My structure looks something like this

@Entity
@Getter
@Setter
public class BaseEntity {
    @EmbeddedId
    private CompositeId compositeId;

    ...
}

@Entity
@Getter
@Setter
public class DerivedEntity extends BaseEntity{
    @OneToMany
    private Set<OtherEntity> entityType;
}

I then try to create views with a similar structure and the issue I seem to be running into is that the collection is mapped using FetchType.SUBSELECT which then causes issues when the query is generated. It seems to stem from the usage of the TREAT keyword which seems to expand to create a case statement, which returns the compositeId of the entity.

When it tries to execute the query, it ends up throwing an UnsupportedOperationException with the message of Resolution of embedded-valued SqmExpressible nodes not yet implemented causing the query to fail to run. It seems that it is unable to resolve the mapping inside of that case statement because it is both an Embeddable and the ID of the source entity.

Expected behavior

The query goes off and the entities are subselected

Actual behavior

An exception is thrown and the query method fails

Steps to reproduce

  1. Create an entity hierarchy using Hibernate that follows the above schema.
  2. Create an identical entity view hierarchy with the collections mapped using FetchType.SUBSELECT
  3. Perform an entity view query using the criteria builder
  4. Observe the exception

Environment

Version: 1.6.12
JPA-Provider: Hibernate
DBMS: Postgres/HSQL
Application Server: Spring Boot

@beikov
Copy link
Member

beikov commented Oct 26, 2024

Hi, the error comes from Hibernate ORM. Can you please share the full stack trace and also the HQL query that is generated which fails?

@retroandchill
Copy link
Author

I can provide a similar version that strips out any information pertinent to the project, but I can at least provide the general idea as to what is happening.

This is the full stack trace from where getResultList() is called in our code.

java.lang.UnsupportedOperationException: Resolution of embedded-valued SqmExpressible nodes not yet implemented

	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.resolveMappingExpressible(MappingMetamodelImpl.java:801)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.determineCurrentExpressible(BaseSqmToSqlAstConverter.java:7017)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSearchedCaseExpression(BaseSqmToSqlAstConverter.java:6983)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSearchedCaseExpression(BaseSqmToSqlAstConverter.java:445)
	at org.hibernate.query.sqm.tree.expression.SqmCaseSearched.accept(SqmCaseSearched.java:118)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelection(BaseSqmToSqlAstConverter.java:2264)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectClause(BaseSqmToSqlAstConverter.java:2206)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:2077)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:445)
	at org.hibernate.query.sqm.tree.select.SqmQuerySpec.accept(SqmQuerySpec.java:124)
	at org.hibernate.query.sqm.spi.BaseSemanticQueryWalker.visitQueryPart(BaseSemanticQueryWalker.java:244)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQueryPart(BaseSqmToSqlAstConverter.java:1935)
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:1620)
	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:785)
	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)
	at com.blazebit.persistence.impl.query.TypedQueryWrapper.getResultList(TypedQueryWrapper.java:49)
	at com.blazebit.persistence.impl.query.ObjectBuilderTypedQuery.getResultList(ObjectBuilderTypedQuery.java:63)
	at com.blazebit.persistence.impl.AbstractQueryBuilder.getResultList(AbstractQueryBuilder.java:58)

As for the HQL query, this is the rough outline of what's going on in the query based on the minimal example provided above:

SELECT CASE
           WHEN TYPE(entity) = DerivedEntity THEN 1
           WHEN TYPE(entity) = OtherDerived THEN 2
           ELSE 0 END                                                                                                 AS BaseEntityView_class,
       entity.compositeId                                                                                               AS BaseEntityView_compositeId,                                                                                                AS NodeView_template_rows,
       CASE
           WHEN TYPE(entity) = DerivedEntity
               THEN entity.compositeId END                                                                              AS BaseEntityView_otherEntities,
       FROM BaseEntity entity
WHERE entity.compositeId = :param_0;

@beikov
Copy link
Member

beikov commented Oct 29, 2024

Ah, I think I see where this is going. Do you use the CompositeId type in the entity view? Try creating an entity view for that embeddable too, then this will work.
This is an unfortunate limitation, but I wouldn't recommend using JPA types in entity views anyway, so it's IMO not that big of a deal.

@retroandchill
Copy link
Author

retroandchill commented Oct 29, 2024

@beikov I just tried that but I got the same result in the same location. It seems like the issue is coming from the generated HQL and since it sees the field being queries as the ID it ends up failing still.

It looks as if the HQL code has not changed at all from that change and is still using the composite field.

@beikov
Copy link
Member

beikov commented Oct 29, 2024

Please share the entity views you're using.

@retroandchill
Copy link
Author

Gladly:
This is the view for the base class:

@EntityView(BaseEntity.class)
@EntityViewInheritance
public interface BaseEntityView {
    @IdMapping
    CompositeIdView getCompositeId();

    int getDataField();
}

Here is the ID

@EntityView(CompositeId.class)
public interface CompositeIdView {
    UUID getId();

    int getRevisionId();
}

This is the one for the derived view:

@EntityView(DerivedEntity.class)
public interface DerivedEntityView extends BaseEntityView {
    @Mapping(fetch = FetchStrategy.SUBSELECT)
    Set<OtherEntityView> getOtherEntities();
}

And this is the one for the related entity:

@EntityView(OtherEntity.class)
public interface OtherEntityView {
    @IdMapping
    UUID getId();

    int getMyDataField();
}

When run this is the query that gets generated:

SELECT CASE WHEN TYPE(baseEntity) = DerivedEntity THEN 1 ELSE 0 END                  AS BaseEntityView_class,
       baseEntity.compositeId.id                                                     AS BaseEntityView_compositeId_id,
       baseEntity.compositeId.revisionId                                             AS BaseEntityView_compositeId_revisionId,
       baseEntity.dataField                                                          AS BaseEntityView_dataField,
       CASE WHEN TYPE(baseEntity) IN (DerivedEntity) THEN baseEntity.compositeId END AS BaseEntityView_otherEntities
FROM BaseEntity baseEntity;

@beikov beikov self-assigned this Oct 29, 2024
@beikov beikov added kind: bug component: entity-view worth: high Implementing this has a high worth labels Oct 29, 2024
@beikov beikov added this to the 1.6.13 milestone Oct 29, 2024
@beikov
Copy link
Member

beikov commented Oct 29, 2024

Thanks, I can imagine where this goes wrong. I hope though, that you can use a different fetch type in the meantime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: entity-view kind: bug worth: high Implementing this has a high worth
Projects
None yet
Development

No branches or pull requests

2 participants