micronaut-projects / micronaut-data

Ahead of Time Data Repositories
Apache License 2.0
464 stars 197 forks source link

JPA repository findById does not work with composite ID set by @IdClass #807

Closed sickfar closed 3 years ago

sickfar commented 3 years ago

Hello!

I need to use composite ID in my app. I use Data Hibernate-JPA implementation.

I cannot use embedded ID, as I need to find by it's parts (like genericId + Id, and I need to have findAllByGenericId So I decided to use @IdClass annotation and split ID by fields.

Unfortunately, Micronaut does not support such composite ID, while Hibernate does.

As WA I will create some additional methods, but it would be nice to have working findById. Thanks.

Steps to Reproduce

Expected Behaviour

Nice found entity by composite ID

Actual Behaviour

Fail with ClassCastException

java.lang.ClassCastException: class example.CompositeKey cannot be cast to class java.lang.Long (example.CompositeKey is in unnamed module of loader 'app'; java.lang.Long is in module java.base of loader 'bootstrap')

    at org.hibernate.type.descriptor.java.LongTypeDescriptor.unwrap(LongTypeDescriptor.java:19)
    at org.hibernate.type.descriptor.sql.BigIntTypeDescriptor$1.doBind(BigIntTypeDescriptor.java:46)
    at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:73)
    at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:276)
    at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:271)
    at org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:53)
    at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:648)
    at org.hibernate.loader.Loader.bindPreparedStatement(Loader.java:2132)
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:2109)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2041)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2019)
    at org.hibernate.loader.Loader.doQuery(Loader.java:948)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
    at org.hibernate.loader.Loader.doList(Loader.java:2850)
    at org.hibernate.loader.Loader.doList(Loader.java:2832)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2664)
    at org.hibernate.loader.Loader.list(Loader.java:2659)
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:506)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:400)
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:219)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1414)
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1565)
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1533)
    at org.hibernate.query.internal.AbstractProducedQuery.uniqueResult(AbstractProducedQuery.java:1575)
    at org.hibernate.query.internal.AbstractProducedQuery.uniqueResultOptional(AbstractProducedQuery.java:1526)
    at io.micronaut.data.hibernate.operations.HibernateJpaOperations.lambda$findOne$2(HibernateJpaOperations.java:173)
    at io.micronaut.transaction.support.AbstractSynchronousTransactionManager.executeRead(AbstractSynchronousTransactionManager.java:155)
    at io.micronaut.data.hibernate.operations.HibernateJpaOperations.findOne(HibernateJpaOperations.java:134)
    at io.micronaut.data.runtime.intercept.DefaultFindOptionalInterceptor.intercept(DefaultFindOptionalInterceptor.java:47)
    at io.micronaut.data.runtime.intercept.DefaultFindOptionalInterceptor.intercept(DefaultFindOptionalInterceptor.java:34)
    at io.micronaut.data.intercept.DataIntroductionAdvice.intercept(DataIntroductionAdvice.java:80)
    at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:82)
    at io.micronaut.validation.ValidatingInterceptor.intercept(ValidatingInterceptor.java:139)
    at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:82)
    at example.EntitiesRepository$Intercepted.findById(Unknown Source)
    at example.DataTest.testCreateReadEntity(DataTest.kt:30)

Environment Information

Example Application

Sorry, I do not have GitHub repo, here is the code

data class CompositeKey(
    var genericId: Long,
    var id: Long
): Serializable {
    constructor() : this(genericId= 0, id = 0)
}

@Entity
@IdClass(CompositeKey::class)
data class Entity(
    @Id
    @Column(name = "generic_id")
    var genericId: Long,
    @Id
    @GeneratedValue
    var id: Long,
    var name: String,
)

@Repository
interface EntitiesRepository : JpaRepository<Entity, CompositeKey>

@MicronautTest
class DataTest {
    @Test
    fun testCreateReadEntity() {
        val savedEntity = repository.save(Entity(
            genericId = 1,
            id = 0,
            name = "New entity"
        ))
        assertTrue(savedEntity .id > 0)
        val found= repository.findById(CompositeKey(1, savedEntity .id))
        assertTrue(found.isPresent)
    }
}
sickfar commented 3 years ago

I have found that AbstractQueryInterceptor does not use HibernateJpaOperations#findOne(@NonNull Class<T>, @NonNull Serializable) That could solve the problem

issmo commented 3 years ago

would it be possible to update to the latest 2.2.0 and confirm this is working?

sickfar commented 3 years ago

@issmo I've just updated to

<micronaut.data.version>2.2.0</micronaut.data.version>

Unfortunately, nothing changed. findById leads to ClassCastException My workaround: findByGenericIdAndId works anyway

issmo commented 3 years ago

I am unable to reproduce the behaviour described, the sole difference is that my code is in java. if you have a repo with tests that reproduce this I might have a look at it.

sickfar commented 3 years ago

Sorry for late response, I was quite busy. I've created a repo and I can confirm the issue is reproduced in it. https://github.com/sickfar/micronaut-data-issue-807

sickfar commented 3 years ago

@issmo please, any updates on this?