How to get hibernate hierarchy for lazy objects

2019-08-29 12:16发布

问题:

Here is my code snippet:

public class Object1 implements Serializable {
    @Id
    @Column(length = 36)
    protected String id;

    @Column(length = 36, insertable = false, updatable = false)
    protected String parentID;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "object2ID", referencedColumnName = "parentID")
    protected List<Object2> parents = new ArrayList<>();

    public List<Object2> getParents() {
        return parents;
    }
}

public class Object2 implements Serializable {
    @Id
    @Column(length = 36)
    protected String id;

    @Column(length = 36, insertable = false, updatable = false)
    protected String object2ID;

    @Column(length = 36, insertable = false, updatable = false)
    protected String parentID;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "object2ID", referencedColumnName = "parentID")
    protected List<Object2> parents = new ArrayList<>();

    public List<Object2> getParents() {
        return parents;
    }
}

and Application class:

public class Application {
    public static Logger logger = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {
        Path HIBERNATE_CONFIGURATION = Paths.get("");
        Configuration configuration = new Configuration().configure(HIBERNATE_CONFIGURATION.toFile());
        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(
                configuration.getProperties()).build();
        SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        Session session = sessionFactory.openSession();
        Object1 object1 = (Object1) session.get(Object1.class, "1");
        logger.info(object1.toString());

        Object2 object2 = object1.getParents().get(0);
        logger.info(object2.toString());

        while (!object2.getParents().isEmpty()) {
            object2 = object2.getParents().get(0);
            logger.info(object2.toString());
        }

        session.close();
    }

}

I am getting Object1 as expected, but object2 throws the exception org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Session is not closed, so why I am getting this error?

I'm using Hibernate core: 4.3.7.Final

Solve:

Hi. Thanks all. I found solve for my problem. I tries get OneToMany, but on real in db reference is type ManyToMany. I create small change for db and model.

I rename object. Here is new code snippet:

@Entity
@Table(name = "Houses")
public class House implements Serializable {
    @Id
    @Column(length = 36)
    protected String id;

    @Column(length = 36)
    protected String parentGUID;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parentguid", referencedColumnName = "guid")
    protected AddressObject address;

    public AddressObject getAddress() {
        return address;
    }

    @Override
    public String toString() {
        return "Object1{" +
                "id='" + id + '\'' +
                ", parentGUID='" + parentGUID + '\'' +
                '}';
    }
}

@Entity
@Table(name = "AddressObjects")
public class AddressObject implements Serializable {
    @Id
    @Column(length = 36)
    protected String id;

    @Column(length = 36, unique = true)
    protected String guid;

    @Column(length = 36, nullable = true)
    protected String parentGUID;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "address")
    protected List<House> houses = new ArrayList<>();

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "upHierarchicObject")
    protected List<AddressObject> downHierarchicObject = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parentguid", referencedColumnName = "guid")
    protected AddressObject upHierarchicObject;

    public List<House> getHouses() {
        return houses;
    }

    public List<AddressObject> getDownHierarchicObject() {
        return downHierarchicObject;
    }

    public AddressObject getUpHierarchicObject() {
        return upHierarchicObject;
    }

    @Override
    public String toString() {
        return "Object2{" +
                "id='" + id + '\'' +
                ", guid='" + guid + '\'' +
                ", parentGUID='" + parentGUID + '\'' +
                '}';
    }
}

and Application class:

public class Application {
    public static Logger logger = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {
        ArrayList<Object> objects = new ArrayList<>();
        Path HIBERNATE_CONFIGURATION = Paths.get("config/hibernate.test.cfg.xml");
        Configuration configuration = new Configuration().configure(HIBERNATE_CONFIGURATION.toFile());
        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(
                configuration.getProperties()).build();
        SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        Session session = sessionFactory.openSession();
        House house = (House) session.get(House.class, "1");
        objects.add(house);

        AddressObject addressObject = house.getAddress();
        objects.add(addressObject);

        while (addressObject.getUpHierarchicObject() != null) {
            addressObject = addressObject.getUpHierarchicObject();
            objects.add(addressObject);
        }

        for (Object obj : objects) {
           logger.info("Object: {}", obj);
        }
        session.close();
    }
}

But, i doesn't why i except LazyInitializationException. This is a bug on hibernate?

回答1:

A Lazy is when you forgot to do a fetch in the query (here for Parents) and you try to retrive the java object after closing the session In this part of the code, how do you know that the object2 comming from the object1 is fetched with the Parents? here we just test null

object1.getParents() != null

but in the case of the lazy, the object is not null but virtual

if you want to use it, you need to have the session opened but at this step the session is already closed because we close the session just after retriving the object from the database

Example :

Session s = sessions.openSession();
Transaction tx = s.beginTransaction();

Employee e = (Employee) s.createQuery("from Employee e where e.name=:empName").setString("empName", eName).uniqueResult();
List roles = u.getRoles();
tx.commit();
s.close();
String role = roles.get(0); //  This line will throw error

 Easy Solution

 Use lazy=false in your Entity class.

Note

Here Session is not closed. This could do the trick

Fetch condition can do the trick fetch="join" or FetchMode.JOIN in many-to-one condition in Entity class The fetch strategy defined in the mapping affects:

retrieval via get() 

Hope this helps.



回答2:

You have to provide dummy loop to fetch all objects first.

For example

 for (Object2 obj:object1.getParents()) {
          obj.getName();
 }