ObjectDB ObjectDB

Mapped (Inverse) LAZY @OneToMany vs. Unmapped LAZY @OneToMany

#1

In the code example below (also attached) I would like to understand why I am able to print to console a lazy @OneToMany mapped field after a finding entity manager closes. There is a switch DO_VIEW_BEFORE_CLOSE, and when it is true the fields are accessed and output before the finding entity manager closes and as expected both a mapped @OneToMany list and an unmapped @OneToMany list are viewable:

a.listA_mapped:[{A}[4]"ownedElement1", {A}[5]"ownedElement2"]
a.listB_unmapped:[{B}[2]"b1", {B}[3]"b2"]

When I set DO_VIEW_BEFORE_CLOSE false, so that the fields are only accessed after the finding entity manager closes, I can still view a.listA_mapped, whereas a.listB_unmapped is not visible/resolved, as one would expect for LAZY:

a.listA_mapped:[{A}[4]"ownedElement1", {A}[5]"ownedElement2"]
a.listB_unmapped:[]

Note that at no time after the find do I access the other mapping side of the relationship a.a.

Q: Why is the LAZY behaviour different for mapped and unmapped lists ?
 

package com.greensoft.objectdb.test.console;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;

/**
*
* @author Darren Kelly (Webel IT Australia for Greensoft)
*/
public class TestLazyOneToMany {

    /**
     * If true the view/output of the fields of the entities will be called
     * before the relevant finding EntityManager closes.
     */
    final static boolean DO_VIEW_BEFORE_CLOSE = true;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String $connection = "objectdb:db/TestLazyOneToMany.tmp;drop";
        EntityManagerFactory emf =
             Persistence.createEntityManagerFactory($connection);

        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        A a = new A("aOwner");

        a.listA_mapped = new ArrayList<A>();
        A aOwnedElement1 = new A("ownedElement1");
        aOwnedElement1.a = a;
        a.listA_mapped.add(aOwnedElement1);
        A aOwnedElement2 = new A("ownedElement2");
        aOwnedElement2.a = a;
        a.listA_mapped.add(aOwnedElement2);

        a.listB_unmapped = new ArrayList<B>();
        a.listB_unmapped.add(new B("b1"));
        a.listB_unmapped.add(new B("b2"));

        em.persist(a);
        em.getTransaction().commit();
        Long id = a.id;
        em.close();

        em = emf.createEntityManager();
        a = em.find(A.class, id);

        if (!DO_VIEW_BEFORE_CLOSE) {
            em.close();
        }

        //Output entities and fields

        System.out.println("a.listA_mapped:" + a.listA_mapped);
            //gives: '[a.listA_mapped:[{A}[4]"ownedElement1", {A}[5]"ownedElement2"]
        System.out.println("a.listB_unmapped:" + a.listB_unmapped);
            //gives: '[]' or '[{B}[2]"b1", {B}[3]"b2"]'

        if (DO_VIEW_BEFORE_CLOSE) {
            em.close();
        }

        emf.close();
    }

    @Entity
    static public class A {

        @Id
        @GeneratedValue
        Long id;
        String name;

        public A(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "{" + getClass().getSimpleName() + "}" + "[" + id + "]\"" + name + "\"";
        }
        @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        A a; //maps: listA_mapped
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "a", cascade = CascadeType.ALL)
        List<A> listA_mapped;
        @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
        List<B> listB_unmapped;
    }

    @Entity
    static public class B extends A {

        public B(String name) {
            super(name);
        }
    }
}
edit
delete
#2

LAZY in JPA is a hint.

If your code is enhanced (as always recommended), for example, you run this test case by:

java -javaagent:objectdb.jar com.greensoft.objectdb.test.console.TestLazyOneToMany

Then ObjectDB follow the hint and the output is:

a.listA_mapped:null
a.listB_unmapped:[]

If the code is not enhanced, ObjectDB has to keep a snapshot of the entity object when it is loaded in order to identify changes on commit. In previous versions of ObjectDB the entity snapshot didn't include inverse (mapped by) fields, but currently it does (it may be changed again in future versions), so currently inverse relationships are always loaded eagerly in entity classes that are not enhanced.

ObjectDB Support
edit
delete
#3

Thanks for your explanation. I was able to confirm the result with enhancer on. I am curious to know (it's purely an academic question) why the mapped list gives null and the unmapped list gives an empty list [], when DO_VIEW_BEFORE_CLOSE = false.

edit
delete
#4

Actually there is no good reason for this. Build 2.3.7_05 removes this difference by setting unloaded collections to null on detachment, for both mapped an unmapped collections.

ObjectDB Support
edit
delete

Reply

To post on this website please sign in.