7
votes

Here's what I'm trying to do.

  1. Create a parent with a OneToOne relation to a child
  2. The parent has to fetch the children using lazy loading
  3. If parent is removed, so is the child
  4. If the child is removed, the parent should not be affected
  5. The cascade update and delete has to be translated into DDL

class Parent

@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL)
public Child getChild()

class Child

@OneToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name="parent_id")
public Parent getParent()

I've got point 1, 3, 4 fully working and Point 5 partially working, still need to solve how to translate the update part indo DDL.

Point 2 is the big issue here, with my current solution the parent does not load the child lazily. The child however does load the parent lazily, but inverting the annotations would mess upp the cascading (points 3, 4 and 5).

I'm very confused right now, hoping I've missed something obvious, so any help would be greatly appreciated.

EDIT: Code requested by Adeel Ansari

'fetch=FetchType.LAZY' has been added to class Parent, otherwise the same as above.

IParentDAO parentDAO = DAOFactory.getFactory().getParentDAO();

parentDAO.beginTransaction();
//findByPrimaryKey uses 'org.hibernate.Session.get(Class clazz, Serializable id)'
parentDAO.findByPrimaryKey(1l);
parentDAO.commitTransaction();

The resulting hibernate queries, one fetching Parent and one fetching Child:

Hibernate: select parent0_.id as id0_0_ from parents parent0_ where parent0_.id=?
Hibernate: select child0_.id as id1_0_, child0_.parent_id as parent2_1_0_ from childs child0_ where child0_.parent_id=?

Here's the code for findByPrimaryKey:

public class HibernateParentDAO extends HibernateDAO<Parent, Long> implements IParentDAO {

    public HibernateParentDAO() {
        super(Parent.class);
    }
}

public abstract class HibernateDAO<T, ID extends Serializable> implements IGenericDAO<T, ID> {
    private Class<T> persistentClass;

    public HibernateDAO(Class<T> c) {
        persistentClass = c;
    }

    @SuppressWarnings("unchecked")
    public T findByPrimaryKey(ID id) {
        return (T) HibernateUtil.getSession().get(persistentClass, id);
    }
}
4

4 Answers

21
votes

I've been having a similar issue. There are a few different solutions, but all of them are workarounds.

The short answer is: Hibernate does NOT support lazy one-to-one relationships.

The long answer (workaround) is:

  1. Declare the relationship to be one-to-one on one side (child), and one-to-many on the other side (parent). Thus a parent.getchild() returns a set, yet it will be able to use lazy loading.

  2. You can try to have the parent and the children to share the primary key, but this would require you to alter the schema.

  3. You can try to configure a view in your database reflecting this one-to-one relationship.

11
votes

[This part doesn't hold anymore]

Modify this in your Parent like below,

@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Child getChild()

Should work.


[Edited to explain why it will not work]

Right after loading B, you may call getCee() to obtain C. But look, getCee() is a method of YOUR class and Hibernate has no control over it. Hibernate does not know when someone is going to call getCee(). That means Hibernate must put an appropriate value into "cee" property at the moment it loads B from database.

If proxy is enabled for C, Hibernate can put a C-proxy object which is not loaded yet, but will be loaded when someone uses it. This gives lazy loading for one-to-one.

But now imagine your B object may or may not have associated C (constrained="false"). What should getCee() return when specific B does not have C? Null. But remember, Hibernate must set correct value of "cee" at the moment it set B (because it does no know when someone will call getCee()). Proxy does not help here because proxy itself in already non-null object.

If your B->C mapping is mandatory (constrained=true), Hibernate will use proxy for C resulting in lazy initialization. But if you allow B without C, Hibernate just HAS TO check presence of C at the moment it loads B. But a SELECT to check presence is just inefficient because the same SELECT may not just check presence, but load entire object. So lazy loading goes away.

Reference: http://community.jboss.org/wiki/Someexplanationsonlazyloadingone-to-one


[Edited to include a workaround]

You can use optional=false and @LazyToOne for the relationship that is not optional. Don't forget to include cascade={CascadeType.PERSIST,CascadeType.REMOVE}. Since, it is obvious for a not-optional relationship. Below is an example,

@OneToOne(mappedBy="parent", optional=false, fetch=FetchType.LAZY, cascade={CascadeType.PERSIST,CascadeType.REMOVE})
@LazyToOne(LazyToOneOption.PROXY)
public Child getChild(){...}

This should work for you, as I can see you are using cascade=CascadeType.ALL, which means not-optional. Isn't it? But for optional relationship, you might like to consider workaround given by iliaden, here.

7
votes

Have you tried @OneToOne(fetch = FetchType.LAZY, optional=false)? Also check this blog and this thread.

-1
votes

@One-to-one relationship not support lazy initialization. To get object you don't put FetchType.LAZY in child class and obtain all child object.

class Parent

@OneToOne(mappedBy = "parent", cascade = CascadeType.REMOVE)
public Child getChild()

class Child

@OneToOne(cascade = CascadeType.REMOVE)
public Parent getParent()