如果从数据库中加载两次,hibernate 是否会加载同一实例的两个单独副本?
不,它不会在同一个 Session 中加载同一个对象两次(为了我们的理智)。
我创建了一个简单的Spring Boot project 来查看这个。
EntityA#setOfB(B1,B2)
EntityB#setOfA(A1)
在通过ID将EntityA 1和EntityB加载到Session之后,我们强制A1.setOfB初始化。
在下面的日志中我们可以看到,尽管它必须查询获取两行(B1 和 B2)的集合,但它只会隐藏一个对象(B2),因为 B1 在会话缓存中找到。见测试 1 (mvn spring-boot:run -Drun.arguments="1")
DEBUG 6452 --- [ main] o.g.hiplay.app.HibernateService : ########## Retrieving A1's set of B ##########
DEBUG 6452 --- [ main] org.hibernate.SQL : select setofb0_.id_a as id_a1_0_0_, setofb0_.id_b as id_b2_2_0_, entityb1_.id as id1_1_1_, entityb1_.description as descript2_1_1_ from relations setofb0_ inner join entityb entityb1_ on setofb0_.id_b=entityb1_.id where setofb0_.id_a=?
TRACE 6452 --- [ main] o.h.l.p.e.i.AbstractLoadPlanBasedLoader : Bound [2] parameters total
DEBUG 6452 --- [ main] o.h.l.p.e.p.i.ResultSetProcessorImpl : Preparing collection intializer : [oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Constructing collection load context for result set [rs3: org.h2.result.LocalResult@5c99abd7 columns: 4 rows: 2 pos: -1]
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Starting attempt to find loading collection [[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]]
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Collection not yet initialized; initializing
TRACE 6452 --- [ main] o.h.l.p.e.p.i.ResultSetProcessorImpl : Processing result set
DEBUG 6452 --- [ main] o.h.l.p.e.p.i.ResultSetProcessorImpl : Starting ResultSet row #0
DEBUG 6452 --- [ main] e.p.i.CollectionReferenceInitializerImpl : Found row of collection: [oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Starting attempt to find loading collection [[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]]
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Attempting to locate loading collection entry [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]] in any result-set context
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Collection [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]] located in load context
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Found loading collection bound to current result set processing; reading row
TRACE 6452 --- [ main] o.h.e.internal.DefaultLoadEventListener : Loading entity: [oss.gabrielgiussi.hiplay.entities.EntityB#1]
TRACE 6452 --- [ main] o.h.e.internal.DefaultLoadEventListener : Attempting to resolve: [oss.gabrielgiussi.hiplay.entities.EntityB#1]
TRACE 6452 --- [ main] o.h.e.internal.DefaultLoadEventListener : Resolved object in session cache: [oss.gabrielgiussi.hiplay.entities.EntityB#1]
DEBUG 6452 --- [ main] o.h.l.p.e.p.i.ResultSetProcessorImpl : Starting ResultSet row #1
TRACE 6452 --- [ main] l.p.e.p.i.EntityReferenceInitializerImpl : hydrating entity state
TRACE 6452 --- [ main] l.p.e.p.i.EntityReferenceInitializerImpl : Initializing object from ResultSet: [oss.gabrielgiussi.hiplay.entities.EntityB#2]
TRACE 6452 --- [ main] o.h.p.entity.AbstractEntityPersister : Hydrating entity: [oss.gabrielgiussi.hiplay.entities.EntityB#2]
DEBUG 6452 --- [ main] e.p.i.CollectionReferenceInitializerImpl : Found row of collection: [oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Starting attempt to find loading collection [[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]]
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Attempting to locate loading collection entry [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]] in any result-set context
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Collection [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]] located in load context
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Found loading collection bound to current result set processing; reading row
TRACE 6452 --- [ main] o.h.e.internal.DefaultLoadEventListener : Loading entity: [oss.gabrielgiussi.hiplay.entities.EntityB#2]
TRACE 6452 --- [ main] o.h.e.internal.DefaultLoadEventListener : Attempting to resolve: [oss.gabrielgiussi.hiplay.entities.EntityB#2]
TRACE 6452 --- [ main] o.h.e.internal.DefaultLoadEventListener : Resolved object in session cache: [oss.gabrielgiussi.hiplay.entities.EntityB#2]
TRACE 6452 --- [ main] o.h.l.p.e.p.i.ResultSetProcessorImpl : Done processing result set (2 rows)
TRACE 6452 --- [ main] o.h.l.p.e.p.internal.AbstractRowReader : Total objects hydrated: 1
DEBUG 6452 --- [ main] o.h.engine.internal.TwoPhaseLoad : Resolving associations for [oss.gabrielgiussi.hiplay.entities.EntityB#2]
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Attempting to locate loading collection entry [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityB.setOfA#2]] in any result-set context
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Collection [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityB.setOfA#2]] not located in load context
TRACE 6452 --- [ main] org.hibernate.type.CollectionType : Created collection wrapper: [oss.gabrielgiussi.hiplay.entities.EntityB.setOfA#2]
DEBUG 6452 --- [ main] o.h.engine.internal.TwoPhaseLoad : Done materializing entity [oss.gabrielgiussi.hiplay.entities.EntityB#2]
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Attempting to locate loading collection entry [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]] in any result-set context
TRACE 6452 --- [ main] o.h.e.loading.internal.LoadContexts : Collection [CollectionKey[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]] located in load context
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Removing collection load entry [org.hibernate.engine.loading.internal.LoadingCollectionEntry<rs=rs3: org.h2.result.LocalResult@5c99abd7 columns: 4 rows: 2 pos: 2, coll=[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]>@131c8e88]
DEBUG 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : 1 collections were found in result set for role: oss.gabrielgiussi.hiplay.entities.EntityA.setOfB
TRACE 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Ending loading collection [org.hibernate.engine.loading.internal.LoadingCollectionEntry<rs=rs3: org.h2.result.LocalResult@5c99abd7 columns: 4 rows: 2 pos: 2, coll=[oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]>@131c8e88]
DEBUG 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : Collection fully initialized: [oss.gabrielgiussi.hiplay.entities.EntityA.setOfB#1]
DEBUG 6452 --- [ main] o.h.e.l.internal.CollectionLoadContext : 1 collections initialized for role: oss.gabrielgiussi.hiplay.entities.EntityA.setOfB
TRACE 6452 --- [ main] o.h.e.i.StatefulPersistenceContext : Initializing non-lazy collections
DEBUG 6452 --- [ main] o.g.hiplay.app.HibernateService : ########## The object hasn't been loaded twice ##########
Hibernate 检查 Object to hydrate 是否已经在会话缓存中 asking to the StatefulPersistenceContext
我现在期望的是实体 B 的实例 bB 也已完全初始化,但不,它没有,当我引用具有实体 A 的实例 aA 的实体 B 的集合时,我得到延迟初始化异常。
真正未初始化的是bB的一组A。在做了类似的事情之后:
EntityA aA = session.get("A",a)
aA.setOfB.size() // 强制初始化惰性集合。此时 bB 处于水合状态并被放入内存,但他对 A 的集合未初始化。
如果您尝试访问集合的元素,Hibernate 需要初始化集合(到目前为止,它所拥有的只是一个代理,可以在需要时从数据库中加载集合,例如请求一个元素或大小集合)。见测试 3 (mvn spring-boot:run -Drun.arguments="2")
// outside of the transaction
EntityB bB = aA.setOfB().get(0)
bB.setOfA().size() // LazyInitializationExample
如果您在事务内部,则集合将被初始化,并且与 aA 对应的行将再次从基中检索,但不会被水合。见测试 3 (mvn spring-boot:run -Drun.arguments="3")