延迟加载不适用于 Kotlin 上的 Hibernate JPA 中的 OneToOne 映射

问题描述 投票:0回答:1

在以下最小可重现示例中,延迟加载对我不起作用。 (我确实需要延迟加载,因为我需要在代码库中的多个地方依赖它。)

我得到的错误是在代码之后:

import jakarta.persistence.*

@Entity
open class X(
    @Id val k: Int,
    @OneToOne(mappedBy = "xField", fetch = FetchType.LAZY)
    var yField: Y? = null
) {
    constructor() : this(0)

    open fun getY(): Y? {
        return yField
    }

    open fun setY(y: Y?) {
        this.yField = y
    }
}

@Entity
open class Y(
    @Id
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "x_k")
    val xField: X,
    val nField: Int
) {
    constructor() : this(X(0), 0)

    open fun getX(): X {
        return xField
    }

    open fun getN(): Int {
        return nField
    }
}

fun main() {
    val entityManagerFactory = Persistence.createEntityManagerFactory("your-persistence-unit")
    val entityManager = entityManagerFactory.createEntityManager()

    try {
        entityManager.transaction.begin()

        val x = entityManager.find(X::class.java, 0) ?: throw IllegalStateException("cannot find X 0")
        println("found X")
        println(x.k)
        println(x.getY())    // Should print proxy or actual Y entity
        println(x.getY()?.getN()) // Should print 7 if Y is correctly lazily loaded

        entityManager.transaction.commit()

    } catch (e: Exception) {
        entityManager.transaction.rollback()
        e.printStackTrace()
    } finally {
        entityManager.close()
        entityManagerFactory.close()
    }
}

以下是运行代码时遇到的两个异常(一个警告和一个错误):

00:38:27.269 [main] WARN org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard - HHH000305: Could not create proxy factory for:X
org.hibernate.HibernateException: Getter methods of lazy classes cannot be final: X#getK
    at org.hibernate.proxy.pojo.ProxyFactoryHelper.validateGetterSetterMethodProxyability(ProxyFactoryHelper.java:80)
    at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.createProxyFactory(EntityRepresentationStrategyPojoStandard.java:241)
    at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.<init>(EntityRepresentationStrategyPojoStandard.java:147)
    at org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandard.resolveStrategy(ManagedTypeRepresentationResolverStandard.java:62)
    at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:512)
    at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:140)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
    at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:92)
    at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:75)
    at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.processBootEntities(MappingMetamodelImpl.java:247)
    at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.finishInitialization(MappingMetamodelImpl.java:185)
    at org.hibernate.internal.SessionFactoryImpl.initializeMappingModel(SessionFactoryImpl.java:321)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:271)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1458)
    at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:55)
    at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:80)
    at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
    at TMainKt.main(tMain.kt:40)
    at TMainKt.main(tMain.kt)


java.lang.NullPointerException: Cannot invoke "org.hibernate.sql.results.graph.Initializer.asEntityInitializer()" because "initializer" is null
    at org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl$CircularFetchAssembler.resolveCircularInitializer(CircularBiDirectionalFetchImpl.java:226)
    at org.hibernate.sql.results.internal.domain.CircularBiDirectionalFetchImpl$CircularFetchAssembler.assemble(CircularBiDirectionalFetchImpl.java:138)
    at org.hibernate.sql.results.graph.embeddable.internal.AbstractNonAggregatedIdentifierMappingInitializer.extractRowState(AbstractNonAggregatedIdentifierMappingInitializer.java:228)
    at org.hibernate.sql.results.graph.embeddable.internal.AbstractNonAggregatedIdentifierMappingInitializer.initializeInstance(AbstractNonAggregatedIdentifierMappingInitializer.java:169)
    at org.hibernate.sql.results.graph.embeddable.internal.EmbeddableAssembler.assemble(EmbeddableAssembler.java:34)
    at org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.initializeIdentifier(AbstractEntityInitializer.java:370)
    at org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.resolveEntityKey(AbstractEntityInitializer.java:347)
    at org.hibernate.sql.results.graph.entity.AbstractEntityInitializer.resolveKey(AbstractEntityInitializer.java:289)
    at org.hibernate.sql.results.internal.InitializersList.resolveKeys(InitializersList.java:82)
    at org.hibernate.sql.results.internal.StandardRowReader.coordinateInitializers(StandardRowReader.java:109)
    at org.hibernate.sql.results.internal.StandardRowReader.readRow(StandardRowReader.java:87)
    at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:179)
    at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:33)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:361)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:168)
    at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:93)
    at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31)
    at org.hibernate.loader.ast.internal.SingleIdLoadPlan.load(SingleIdLoadPlan.java:146)
    at org.hibernate.loader.ast.internal.SingleIdLoadPlan.load(SingleIdLoadPlan.java:106)
    at org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyPropertiesFromDatastore(AbstractEntityPersister.java:1552)
    at org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyProperty(AbstractEntityPersister.java:1497)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.lambda$loadAttribute$0(LazyAttributeLoadingInterceptor.java:112)
    at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper.performWork(EnhancementHelper.java:206)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.loadAttribute(LazyAttributeLoadingInterceptor.java:82)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.fetchAttribute(LazyAttributeLoadingInterceptor.java:78)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.handleRead(LazyAttributeLoadingInterceptor.java:59)
    at org.hibernate.bytecode.enhance.spi.interceptor.AbstractInterceptor.readObject(AbstractInterceptor.java:152)
    at X.$$_hibernate_read_yField(tMain.kt)
    at X.getY(tMain.kt:12)
    at TMainKt.main(tMain.kt:49)
    at TMainKt.main(tMain.kt)

Process finished with exit code 0

这是我的 build.gradle.kts 文件:

plugins {
    kotlin("jvm") version "2.0.0"
    id("org.hibernate.orm") version "6.2.28.Final"
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.hibernate:hibernate-core:6.2.28.Final")
    implementation("jakarta.persistence:jakarta.persistence-api:3.1.0")
    implementation("org.postgresql:postgresql:42.5.0")
    implementation("ch.qos.logback:logback-classic:1.2.11")
}

hibernate {
    enhancement {
        enableLazyInitialization.set(true)
        enableDirtyTracking.set(true)
        enableAssociationManagement.set(true)
        enableExtendedEnhancement.set(true)
    }
}

tasks.withType<JavaCompile> {
    options.compilerArgs.add("-Xplugin:org.hibernate.orm.enhance")
}

持久性.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
    <persistence-unit name="your-persistence-unit" transaction-type="RESOURCE_LOCAL">
        <class>X</class>
        <class>Y</class>
        <properties>
            <!-- JDBC Database Connection Settings -->
            <property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/main"/>
            <property name="jakarta.persistence.jdbc.user" value="main"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>

            <!-- Hibernate Properties -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

和应用程序属性:

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

以及数据库中的行:

main=> \d+ x
                                            Table "public.x"
 Column |  Type   | Collation | Nullable | Default | Storage | Compression | Stats target | Description 
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
 k      | integer |           | not null |         | plain   |             |              | 
Indexes:
    "x_pkey" PRIMARY KEY, btree (k)
Referenced by:
    TABLE "y" CONSTRAINT "fkl54y40sycpm07uk682c1k3ivs" FOREIGN KEY (x_k) REFERENCES x(k)
Access method: heap

main=> \d+ y
                                            Table "public.y"
 Column |  Type   | Collation | Nullable | Default | Storage | Compression | Stats target | Description 
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
 nfield | integer |           | not null |         | plain   |             |              | 
 x_k    | integer |           | not null |         | plain   |             |              | 
Indexes:
    "y_pkey" PRIMARY KEY, btree (x_k)
Foreign-key constraints:
    "fkl54y40sycpm07uk682c1k3ivs" FOREIGN KEY (x_k) REFERENCES x(k)
Access method: heap

main=> select * from x ;
 k 
---
 0
(1 row)

main=> select * from y ;
 nfield | x_k 
--------+-----
      7 |   0
(1 row)

我已经广泛尝试了各种方法,但就是行不通。


更新:我通过将所有字段设置为

open
来消除第一个异常。下面的代码只给出了第二个例外:

import jakarta.persistence.*

@Entity
open class X(
    @Id open val k: Int,
    @OneToOne(mappedBy = "xField", fetch = FetchType.LAZY)
    open var yField: Y? = null
) {
    constructor() : this(0)

    open fun getY(): Y? {
        return yField
    }

    open fun setY(y: Y?) {
        this.yField = y
    }
}

@Entity
open class Y(
    @Id
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "x_k")
    open val xField: X,
    open val nField: Int
) {
    constructor() : this(X(0), 0)

    open fun getX(): X {
        return xField
    }

    open fun getN(): Int {
        return nField
    }
}

fun main() {
    val entityManagerFactory = Persistence.createEntityManagerFactory("your-persistence-unit")
    val entityManager = entityManagerFactory.createEntityManager()

    try {
        entityManager.transaction.begin()

        val x = entityManager.find(X::class.java, 0) ?: throw IllegalStateException("cannot find X 0")
        println("found X")
        println(x.k)
        println(x.getY())    // Should print proxy or actual Y entity
        println(x.getY()?.getN()) // Should print 7 if Y is correctly lazily loaded

        entityManager.transaction.commit()

    } catch (e: Exception) {
        entityManager.transaction.rollback()
        e.printStackTrace()
    } finally {
        entityManager.close()
        entityManagerFactory.close()
    }
}
kotlin hibernate jpa orm
1个回答
0
投票

哇,有时,就像升级到最新的稳定版本一样简单。我将

hibernate
工件版本更改为
6.5.2.Final
,延迟加载开始完美运行!

将这个问题留在这里,因为这通常是一件棘手的事情,其他人可能会遇到困难。 (ChatGPT-4o 和 Claude 都被难住了!)

© www.soinside.com 2019 - 2024. All rights reserved.