问题
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.
- Once the
FetchType.LAZY
is removed, everything works correctly. - This didn't happen in Java.
- I tried to add
open
on thevar
so that theEvent
can be correctly proxied. No effect. - All the
@Entity
classes are of courseopen
as well. If theopen
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