2

My understanding is that, by default, Hibernate will set the FetchType to lazy, for all relation types.

In my case, I have a bi-directional OneToMany-ManyToOne relationship, as follows:

Parent class:

public class Parent{

    @Id
    @Column(name = "parent_id")
    private long parentId;

    @OneToMany(mappedBy = "parent")
    @JsonIgnore
    private List<Child> children;

    public List<Child> getChildren()
    {
        return this.children;
    }
}

Child class:

public class Child
{
    private String name;

    @ManyToOne(optional = false) //every child must have a parent
    @JoinColumn(name = "parent_id")
    private Parent parent;

    public Child(Parent parent, String name)
    {
        this.parent = parent;
        this.name = name;
    }
}

Child service:

public class ChildService
{
    public List<Child> getChildren(long parentId)
    {
        Parent parent = getParentRepository().findOne(parentId);
        return parent.getChildren(); //This returns null
    }

    public Child getNamedChild(long parentId)
    {
        Parent parent = getParentRepository().findOne(parentId);
        //??????????????? 
        //Not sure how to get the children of this specific parent, 
        //which has a specific name.
    }
}

Parent repository:

public interface ParentRepository extends JpaRepository<Parent, Long> {
}

I have two problems here:

When I call the method getChildren() in ChildService, it returns null. The database does contain the right data. I will need to get these with LAZY fetch type. I have tried to invoke a "parent.getChildren().size()" method, before returning, but it still returns null.

The other problem is how do I get a child that has a specific name? Is it possible to do it via the Repository? Or do I need to getChildren(parentId), and iterate until I find the one that is named in a specific way?

EDIT: After some suggestions, I went on and implemented my ChildRepository this way:

public interface ChildRepository extends JpaRepository<Child, Long> {
    @Query("SELECT child FROM Parent parent JOIN parent.child AS child WHERE parent.parentId = :parentId")
    List<Child> getChildren(@Param("parentId") String parentId);
    @Query("SELECT child FROM Parent parent JOIN arent.child AS child WHERE parent.parentId = :parentId AND child.childId = :childId")
    Child getChildByName(@Param("childId") Long childId, @Param("parentId") String parentId);
}
7
  • The fetch type on Hibernate is lazy by default only for to many relationships, in a OneToOne by default it is eager. However, if you care about performance, you should always set the fetch type to lazy (also on OneToOne). You can explicitly load lazy relationships with a join fetch (either in named query or criteria). Commented Aug 8, 2017 at 14:28
  • I do not have a OneToOne relationship here. Can you show me, please, how you would load the lazy with the join fetch? I would need it to be in my repository. Commented Aug 8, 2017 at 14:30
  • Are you using transactions? Lazily loaded relationships need to be loaded in the same transaction as the entity they originate from Commented Aug 8, 2017 at 14:33
  • I do not have explicit transaction set. But if this is true, doesn't it defeat the purpose of Lazy fetching? Transactions are supposed to be short. And you do lazy fetching, so you gain performance, things proceed faster, and whenever you may need the children, you will get them in the future? Commented Aug 8, 2017 at 14:36
  • how do you save your data? Commented Aug 8, 2017 at 14:43

1 Answer 1

3

Can you inject the EntityManager in the ChildService class? I cannot fully verify the code below, but you could try something like

public class ChildService
{
    private EntityManager em;

    public List<Child> getChildren(long parentId)
    {
        Query query = em.createQuery("SELECT c FROM Parent p JOIN FETCH p.children AS c WHERE p.parentId = :parentId");
        query.setParameter("parentId", parentId);
        return (List<Child>) q.getResultList();
    }

    public Child getNamedChild(long parentId, String name)
    {
        Query query = em.createQuery("SELECT c FROM Parent p JOIN FETCH p.children AS c WHERE p.parentId = :parentId AND c.name = :name");
        query.setParameter("parentId", parentId);
        query.setParameter("name", name);
        return (Child) q.getSingleResult();
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, I will accept this as best answer, even though it wasn't right that, but it was close. There are two points to mention: If I used JOIN FETCH, I would get this compilation exception: "Query specified join fetching, but the owner of the fetched association was not present in the select list". Secondly, I do not work with the EntityManager, which is being taken care of, by the JpaRepository. I have edited my OP, to reflect the exact changes I have done. Thanks again!
About the JOIN FETCH exception, other Stack Overflow posts suggest JOIN FETCH only makes sense if you fetch the original entity and the relationship at the same time (as I had in my mind). If you only need the relationship (as in your case), you should remove the "FETCH" word as you did in your solution. About EntityManager and JpaRepository, you could use them at the same time. In my code I am step by step replacing the JpaRepository by the EntityManager. EntityManager is somewhat lower level, but it gives more control. It is a choice, your approach is fine as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.