Hibernate with Kotlin: @ManyToOne(fetch = FetchType.LAZY)

不打扰是莪最后的温柔 提交于 2021-02-07 07:32:12

问题


I am using Hibernate with Kotlin and I am having an issue with FetchType.LAZY on @ManyToOne relations. Consider following:

@ManyToOne(fetch = FetchType.LAZY)
open var event: Event?

The problem is that when FetchType.LAZY is used, the fetched Event will be of class Event_$$_jvst_... with JavaassistLazyInitializer on it. But the event will never be initialized, everything will be null or empty.

  1. Once the FetchType.LAZY is removed, everything works correctly.
  2. This didn't happen in Java.
  3. I tried to add open on the var so that the Event can be correctly proxied. No effect.
  4. All the @Entity classes are of course open as well. If the open keyword is removed, there will be no proxy created and so no laziness.

My guess is that Hibernate cannot easily proxy these default kotlin getters. Is there a way to solve it?


回答1:


you could use this static method to deproxy your entity

/**
 * Utility method that tries to properly initialize the Hibernate CGLIB
 * proxy.
 * @param <T>
 * @param maybeProxy -- the possible Hibernate generated proxy
 * @param baseClass -- the resulting class to be cast to.
 * @return the object of a class <T>
 * @throws ClassCastException
 */
public static <T> T deproxy(Object maybeProxy, Class<T> baseClass) throws ClassCastException {
    if (maybeProxy instanceof HibernateProxy) {
        return baseClass.cast(((HibernateProxy) maybeProxy).getHibernateLazyInitializer().getImplementation());
    }
    return baseClass.cast(maybeProxy);
}



回答2:


I write a simple example to check your problem, and all works fine.

import org.hibernate.CacheMode
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.hibernate.Transaction
import org.hibernate.boot.MetadataSources
import org.hibernate.boot.registry.StandardServiceRegistryBuilder
import org.hibernate.cfg.Environment
import java.util.*
import javax.persistence.*


fun main(args: Array<String>) {
    val standardServiceRegistryBuilder = StandardServiceRegistryBuilder()

    val settings = HashMap<String, String>().apply {
        put(Environment.DRIVER, "org.h2.Driver")
        put(Environment.URL, "jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1")
        put(Environment.USER, "sa")
        put(Environment.PASS, "sa")
        put(Environment.DIALECT, "org.hibernate.dialect.H2Dialect")
        put(Environment.SHOW_SQL, "true")
        put(Environment.HBM2DDL_AUTO, "create")
    }

    val sessionFactory = standardServiceRegistryBuilder.applySettings(settings)
            .build()
            .let {
                MetadataSources(it).apply {
                    addAnnotatedClass(History::class.java)
                    addAnnotatedClass(Event::class.java)
                }
            }
            .run { metadataBuilder.build() }
            .run { sessionFactoryBuilder.build() }

    sessionFactory.inSession {
        inTransaction { session ->
            session.save(Event(1, "event description"))

            session.save(History(1, Event(1), "history description"))
        }
    }

    sessionFactory.inSession {
        inTransaction { session ->
            val entity = session.get(Event::class.java, 1L)

            println("=============1=============")
            println(entity)
        }
    }

    sessionFactory.inSession {
        inTransaction { session ->
            val entity = session.load(History::class.java, 1L)

            println("=============2=============")
            println(entity)
        }
    }
}

private fun SessionFactory.inSession(function: Session.() -> Unit) {
    val session = this.openSession()

    session.function()

    session.close()
}

private fun Session.inTransaction(function: Transaction.(s: Session) -> Unit) {
    val transaction = this.beginTransaction()

    transaction.function(this)

    transaction.commit()
}

@Entity
open class History(
        @Id
        open var id: Long? = null,

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "eventId")
        open var event: Event? = null,

        open var description: String = ""
) {
    override fun toString(): String {
        return "History(id=$id, event=$event, description='$description')"
    }
}

@Entity
open class Event(
        @Id
        open var id: Long? = null,
        open var description: String? = null,

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "event")
        open var history: MutableSet<History>? = null
) {
    override fun toString(): String {
        return "Event(id=$id, description='$description', history=${history?.size})"
    }
}

Logs looks like this:

2017-12-05 18:43:03 [main] INFO  org.hibernate.Version - HHH000412: Hibernate Core {5.2.12.Final}
2017-12-05 18:43:03 [main] INFO  org.hibernate.cfg.Environment - HHH000206: hibernate.properties not found
2017-12-05 18:43:03 [main] INFO  o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2017-12-05 18:43:04 [main] WARN  o.hibernate.orm.connections.pooling - HHH10001002: Using Hibernate built-in connection pool (not for production use!)
2017-12-05 18:43:04 [main] INFO  o.hibernate.orm.connections.pooling - HHH10001005: using driver [org.h2.Driver] at URL [jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1]
2017-12-05 18:43:04 [main] INFO  o.hibernate.orm.connections.pooling - HHH10001001: Connection properties: {password=****, user=sa}
2017-12-05 18:43:04 [main] INFO  o.hibernate.orm.connections.pooling - HHH10001003: Autocommit mode: false
2017-12-05 18:43:04 [main] INFO  o.h.e.j.c.i.DriverManagerConnectionProviderImpl - HHH000115: Hibernate connection pool size: 20 (min=1)
2017-12-05 18:43:04 [main] INFO  org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2017-12-05 18:43:04 [main] INFO  o.h.validator.internal.util.Version - HV000001: Hibernate Validator 5.3.5.Final
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by javassist.util.proxy.SecurityActions (file:/Users/evgenyzaharov/.gradle/caches/modules-2/files-2.1/org.javassist/javassist/3.20.0-GA/a9cbcdfb7e9f86fbc74d3afae65f2248bfbf82a0/javassist-3.20.0-GA.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of javassist.util.proxy.SecurityActions
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Hibernate: drop table Event if exists
2017-12-05 18:43:04 [main] INFO  org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@56913163] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate: drop table History if exists
Hibernate: create table Event (id bigint not null, description varchar(255), primary key (id))
2017-12-05 18:43:04 [main] INFO  org.hibernate.orm.connections.access - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@e8e0dec] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate: create table History (id bigint not null, description varchar(255), eventId bigint, primary key (id))
Hibernate: alter table History add constraint FK2yaqfgh2x1lsxcpbuifmd245k foreign key (eventId) references Event
2017-12-05 18:43:04 [main] INFO  o.h.t.s.internal.SchemaCreatorImpl - HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@6c15e8c7'
Hibernate: select event_.id, event_.description as descript2_0_ from Event event_ where event_.id=?
Hibernate: insert into Event (description, id) values (?, ?)
Hibernate: insert into History (description, eventId, id) values (?, ?, ?)
Hibernate: update History set description=?, eventId=? where id=?
Hibernate: select event0_.id as id1_0_0_, event0_.description as descript2_0_0_ from Event event0_ where event0_.id=?
=============1=============
Hibernate: select history0_.eventId as eventId3_1_0_, history0_.id as id1_1_0_, history0_.id as id1_1_1_, history0_.description as descript2_1_1_, history0_.eventId as eventId3_1_1_ from History history0_ where history0_.eventId=?
Event(id=1, description='event description', history=1)
=============2=============
Hibernate: select history0_.id as id1_1_0_, history0_.description as descript2_1_0_, history0_.eventId as eventId3_1_0_ from History history0_ where history0_.id=?
Hibernate: select event0_.id as id1_0_0_, event0_.description as descript2_0_0_ from Event event0_ where event0_.id=?
Hibernate: select history0_.eventId as eventId3_1_0_, history0_.id as id1_1_0_, history0_.id as id1_1_1_, history0_.description as descript2_1_1_, history0_.eventId as eventId3_1_1_ from History history0_ where history0_.eventId=?
History(id=1, event=Event(id=1, description='event description', history=1), description='history description')

Lazy initialisation start to load field data only after explicit getting a value.

Hope this will help you.



来源:https://stackoverflow.com/questions/46373702/hibernate-with-kotlin-manytoonefetch-fetchtype-lazy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!