How to fix JPA or Hibernate BasicAttributeMapping cannot be cast to class TableGroupJoinProducer Snippet

Updated: May 18, 2024

Coding Example

User.java
@Entity
@Table(name = "user")
public class User {
    @Id
    @Column(name = "id", nullable = false)
    private Long id;

    @Size(max = 255)
    @Column(name = "first_name")
    private String firstName;

    @Size(max = 255)
    @Column(name = "last_name")
    private String lastName;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private Set<Role> roles;

}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {

    @Query("""
        FROM User user
        // The issue occurs because the query incorrectly joins the "lastName" column instead of the intended "role" entity association.
        JOIN user.lastName 
    """)
    List<User> userDetail();
}
Error.log
java.lang.ClassCastException: class org.hibernate.metamodel.mapping.internal.BasicAttributeMapping cannot be cast to class org.hibernate.sql.ast.tree.from.TableGroupJoinProducer (org.hibernate.metamodel.mapping.internal.BasicAttributeMapping and org.hibernate.sql.ast.tree.from.TableGroupJoinProducer are in unnamed module of loader 'app')
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.consumeAttributeJoin(BaseSqmToSqlAstConverter.java:3317) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.consumeExplicitJoin(BaseSqmToSqlAstConverter.java:3234) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.lambda$consumeExplicitJoins$43(BaseSqmToSqlAstConverter.java:3205) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1597) ~[na:na]
	at org.hibernate.query.sqm.tree.domain.AbstractSqmFrom.visitSqmJoins(AbstractSqmFrom.java:260) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.consumeExplicitJoins(BaseSqmToSqlAstConverter.java:3201) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.consumeJoins(BaseSqmToSqlAstConverter.java:2864) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.consumeFromClauseRoot(BaseSqmToSqlAstConverter.java:2822) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1597) ~[na:na]
	at org.hibernate.query.sqm.tree.from.SqmFromClause.visitRoots(SqmFromClause.java:80) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitFromClause(BaseSqmToSqlAstConverter.java:2573) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:2055) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:440) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.tree.select.SqmQuerySpec.accept(SqmQuerySpec.java:127) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.spi.BaseSemanticQueryWalker.visitQueryPart(BaseSemanticQueryWalker.java:218) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQueryPart(BaseSqmToSqlAstConverter.java:1915) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:1600) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:440) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.tree.select.SqmSelectStatement.accept(SqmSelectStatement.java:228) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.translate(BaseSqmToSqlAstConverter.java:776) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.buildCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:402) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:327) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:303) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:509) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:427) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.hibernate.query.Query.getResultList(Query.java:120) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
	at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:129) ~[spring-data-jpa-3.2.5.jar:3.2.5]
	at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:92) ~[spring-data-jpa-3.2.5.jar:3.2.5]
	at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:149) ~[spring-data-jpa-3.2.5.jar:3.2.5]
	at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:137) ~[spring-data-jpa-3.2.5.jar:3.2.5]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:164) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70) ~[spring-data-commons-3.2.5.jar:3.2.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-6.1.6.jar:6.1.6]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:392) ~[spring-tx-6.1.6.jar:6.1.6]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135) ~[spring-data-jpa-3.2.5.jar:3.2.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.6.jar:6.1.6]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) ~[spring-aop-6.1.6.jar:6.1.6]
	at jdk.proxy4/jdk.proxy4.$Proxy166.listUser(Unknown Source) ~[na:na]
	at com.onlineappzone.demo.service.UserServiceImpl.userDetail(UserServiceImpl.java:43) ~[classes/:na]
	at com.onlineappzone.demo.controller.UserController.userDetail(UserController.java:27) ~[classes/:na]

Solution

One easy way to solve this problem is to make sure you’re joining the correct entity together. In the query, instead of joining with a column like “user.lastName,” join with the Role table, like “user.role”.

UserRepository.java

public interface UserRepository extends JpaRepository<User, Long> {

    @Query("""
        FROM User user
        JOIN user.role
    """)
    List<User> userDetail();
}