Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does hibernate load two seprate copies of same instance if they are loaded twice from database?

Tags:

java

hibernate

I know there are many different question on SO regarding lazy load but mine one is a bit different.

Lets say i have an entity A in which i have collection of Entity B. Similarly, in Entity B, i have collection of A. In both case, lazy="true" option is used.

Entity A's instace aA has -->Set<B>===(This set contains Entity B's instance bB)

Entity B's instace bB has -->Set<A>===(This set contains Entity A's instance aA)

Now if i load Entity A's collection(Which is Set<B> ).Its initialized now, i.e. complete A's aA instance including collection. What i expect now is Entity B's instance bB is also completely initialized but no, its not and i get lazy initialization exception when i refer Entity B's collection which has Entity A's instance aA.

Does hibernate load two seprate copies of same instance if they are loaded twice from database? if so, is there a way to synchronize changes in all copies within session?

Hope i am clear enough and haven't messed things up with messy info:)

like image 797
Zai Avatar asked Mar 16 '23 15:03

Zai


1 Answers

Does hibernate load two seprate copies of same instance if they are loaded twice from database?

No, it won't load the same object twice (for our sanity's sake) in the same Session.

I've created a simple Spring Boot project to see this.

EntityA#setOfB(B1,B2)
EntityB#setOfA(A1)

After load EntityA 1 and EntityB to Session by ID, we force A1.setOfB initialization. In the log below we can see that altough it has to query the collection obtaining both rows (B1 & B2), it will only hidrate one object (B2) because B1 is found in the Session Cache. See test 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 checks if the Object to hydrate is already in the Session Cache asking to the StatefulPersistenceContext

What i expect now is Entity B's instance bB is also completely initialized but no, its not and i get lazy initialization exception when i refer Entity B's collection which has Entity A's instance aA.

What is truly uninitialized is bB's set of A. After doing something like:

EntityA aA = session.get("A",a) aA.setOfB.size() // force initialization of lazy collection. At this point bB is hydratate and put in memory, but his collection of A is uninitalized.

enter image description here

If you try to access an element of the collection Hibernate needs to initialize the collection (all it has until now is a proxy with the intelligence of load the collection from database when is needed, e.g. asking for an element or the size of the collection). See test 3 (mvn spring-boot:run -Drun.arguments="2")

// outside of the transaction
EntityB bB = aA.setOfB().get(0)
bB.setOfA().size() // LazyInitializationExample

enter image description here

If you were inside of a transaction the collection will be initialized and the row corresponding to aA will be retrieved from the base again but it will not be hydrated. See test 3 (mvn spring-boot:run -Drun.arguments="3")

enter image description here

like image 141
gabrielgiussi Avatar answered Apr 27 '23 15:04

gabrielgiussi