Is it possible to define LEFT Join when annotating a relationship with @OneToMany?
Asked Answered
K

2

6

I have the following scenario

@Entity
public class Parent {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;

    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
    private Set<Child> children;
   }
@Entity
public class Child {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch=FetchType.LAZY, optional = false)
    private Parent parent;
}

When building queries with either JPA Criteria or JPQL I get a inner join by default when requesting a join fetch for children.

Either by using:

SELECT p from Parent p JOIN p.children

OR

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Parent> query = cb.createQuery(Parent.class);

Root<Parent> root = query.from(Parent.class);
root.fetch(Parent_.children);
query.distinct(true);

List<Parent> result = em.createQuery(query).getResultList();

How I can avoid this default behavior(inner join)? My goal is to get LEFT JOIN by default when specifying OneToMany relationship... Is it possible without setting JoinType.LEFT explicitly when building a query?

Any help is really appreciated. Thank you in advance

Kimura answered 13/2, 2015 at 13:55 Comment(0)
F
4

Is it possible without setting JoinType.LEFT explicitly when building a query?

No, it's not! The implicit join is the INNER JOIN which is assumed if you navigate the association in the JPQL query.

You have to use the LEFT JOIN and FETCH in case you need the child association to be fetched as well:

SELECT p 
from Parent p 
LEFT JOIN FETCH p.children

or with Criteria API:

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Parent> query = cb.createQuery(Parent.class);

Root<Parent> root = query.from(Parent.class);
root.fetch(Parent_.children, JoinType.LEFT);
query.distinct(true);

List<Parent> result = em.createQuery(query).getResultList();
Fussbudget answered 13/2, 2015 at 14:58 Comment(4)
Hi Vlad... I have even added this in the question... "Is it possible without setting JoinType.LEFT explicitly when building a query?". I'm trying to find something that could be done using Annotations.. Thanks for your answerKimura
If you set them to lazy you are fine with a secondary select. Otherwise a ln EAGER fetch will inner join it.Fussbudget
I'm just wondering if it is possible to get a default JoinType based on some annotation or annotation's parameter.... for example: root.fetch(Parent_.children); In this snippet that I have added in the question... will it be always defined as JoinType.INNER? Isn't it possible to change this default behavior?Kimura
No, it is not possible. When you fetch you either inner join by default or specify a left outer join explicitly.Fussbudget
B
-1

You could give Blaze-Persistence a try as it does implicit joins the way you'd expect it, i.e. only does an inner join if the row count is preserved.

List<Parent> result = cbf.create(em, Parent.class).fetch("children").getResultList();

generates

SELECT p 
FROM Parent p 
LEFT JOIN FETCH p.children
Butterbur answered 22/11, 2017 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.