# OneToMany association with Lazy Loading and Cascade.ALL

**URL:** <https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185>\
**Category:** Hibernate ORM\
**Created:** [January 31, 2019, 3:45pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185 "2019-01-31T15:45:57Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![afabris\_supapass](https://avatars.discourse-cdn.com/v4/letter/a/5e9695/32.png) [@afabris\_supapass](https://discourse.hibernate.org/u/afabris_supapass)\
**Post date:** [January 31, 2019, 3:45pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/1 "2019-01-31T15:45:57Z")

</div>

Hi.

I’m observing a strange behaviour that I’m not able to explain in Hibernate 5.2.17

I have an entity with several oneToMany collections in it.  
All these collections are traversable in a bi-directional way  
All of them are marked as LazyLoading and Cascade.ALL (we introduced the latter because it helps graphql generation).

When a detached entity of this type is being saved - without changing or accessing any of the associated collections - , hibernate triggers a load (that is fine) but it includes also the first (eg: the entity with the name that is first in lexical ordering) collection in the generated sql.  
I do really don’t want to access an associated entity if it was not changed in the code and I expect Lazy Loading to prevent this.  
Why Hibernate has this behaviour?  
Is there a way to avoid this still using the cascade.all annotation?

Many thanks in advance

---

<div class="post-metadata">

**Author:** ![vlad](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/vlad/32/658_2.png) [@vlad](https://discourse.hibernate.org/u/vlad)\
**Post date:** [January 31, 2019, 4:26pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/2 "2019-01-31T16:26:34Z")

</div>

In order to answer your question, you need to add the entities, the data access code and the SQL logs generated by Hibernate.

---

<div class="post-metadata">

**Author:** ![vlad](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/vlad/32/658_2.png) [@vlad](https://discourse.hibernate.org/u/vlad)\
**Post date:** [January 31, 2019, 5:17pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/4 "2019-01-31T17:17:24Z")

</div>

Try to replicate it with this [test case](http://in.relation.to/2016/01/14/hibernate-jpa-test-case-template/), as it’s not clear why that join is generated from the mappings alone.

---

<div class="post-metadata">

**Author:** ![afabris\_supapass](https://avatars.discourse-cdn.com/v4/letter/a/5e9695/32.png) [@afabris\_supapass](https://discourse.hibernate.org/u/afabris_supapass)\
**Post date:** [February 2, 2019, 5:15pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/5 "2019-02-02T17:15:44Z")

</div>

Thanks Vlad. I’ll replicate with a testcase and update the post

---

<div class="post-metadata">

**Author:** ![afabris\_supapass](https://avatars.discourse-cdn.com/v4/letter/a/5e9695/32.png) [@afabris\_supapass](https://discourse.hibernate.org/u/afabris_supapass)\
**Post date:** [February 3, 2019, 9:53am UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/6 "2019-02-03T09:53:24Z")

</div>

Hi.  
Here you can find and example tescase (some help with this also from a colleague)

> **[darknight74/hibernate-test-case-templates](https://github.com/darknight74/hibernate-test-case-templates/tree/master/orm/hibernate-orm-5)**
>
> Templates and examples to report issues to Hibernate - darknight74/hibernate-test-case-templates

The testcase used is the JPA one.

Looking at the Hibernate logs it’s possible to see that the ORM is trying to load information for the first set of entities, not the second.

Thanks

---

<div class="post-metadata">

**Author:** ![vlad](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/vlad/32/658_2.png) [@vlad](https://discourse.hibernate.org/u/vlad)\
**Post date:** [February 3, 2019, 2:16pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/7 "2019-02-03T14:16:53Z")

</div>

Based on your test case:

```auto
Parent parent = new Parent();
parent.setId(1L);
//when merging hibernate core calls a load that retrieves also data from the first child (Child and not Nephew) - see hibernate logs
entityManager.merge(parent);

```

The SELECT is expected since that’show `merge` works. For a detailed explanation check out these articles:

- [How do persist and merge work in JPA](https://vladmihalcea.com/jpa-persist-and-merge/)
- [How to merge entity collections with JPA and Hibernate](https://vladmihalcea.com/merge-entity-collections-jpa-hibernate/)

---

<div class="post-metadata">

**Author:** ![afabris\_supapass](https://avatars.discourse-cdn.com/v4/letter/a/5e9695/32.png) [@afabris\_supapass](https://discourse.hibernate.org/u/afabris_supapass)\
**Post date:** [February 3, 2019, 8:25pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/8 "2019-02-03T20:25:46Z")

</div>

Thanks for the further explanation.  
Anyway I appreciate that being called a merge the persistence engine first checks for any change in the underlying table so it performs a select.  
But why it includes in a select an associated table (child) even if no change was made on it in the code and it’s lazy loaded. And why nephew is not included in the select even if is defined exactly as child (lazy loaded with cascade.all)?

---

<div class="post-metadata">

**Author:** ![vlad](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/vlad/32/658_2.png) [@vlad](https://discourse.hibernate.org/u/vlad)\
**Post date:** [February 3, 2019, 9:20pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/9 "2019-02-03T21:20:09Z")

</div>

First, the mapping is wrong since it should be like this:

```auto
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Child> children = new HashSet<>();

@OneToMany(mappedBy = "uncle", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Nephew> nephews = new HashSet<>();

```

Notice the `mappedBy` attribute needed by this bidirectional association.

Anyway, this does not affect the OUTER JOIN.

> But why it includes in a select an associated table (child) even if no change was made on it in the code and it’s lazy loaded.

Because it needs to load the entire graph in order to detect changes that need to be propagated via Cascade. If you don’t want the JOIN, you either have to call `persist` or remove the `Cascade`.

> And why nephew is not included in the select even if is defined exactly as child (lazy loaded with cascade.all)?

The executed query is this one:

```
select
    parent0_.id as id1_2_1_,
    parent0_.last_name as last_nam2_2_1_,
    parent0_.name as name3_2_1_,
    children1_.parent_id as parent_i4_0_3_,
    children1_.Id as Id1_0_3_,
    children1_.Id as Id1_0_0_,
    children1_.last_name as last_nam2_0_0_,
    children1_.name as name3_0_0_,
    children1_.parent_id as parent_i4_0_0_ 
from
    parent parent0_ 
left outer join
    Child children1_ 
        on parent0_.id=children1_.parent_id 
where
    parent0_.id=?

```

Now, I debugged the `CascadeEntityJoinWalker` class and it seems it restrict the OUTER JOIN count to avoid Cartesian Products. If the `nephew` association is needed, it will trigger a secondary query.

---

<div class="post-metadata">

**Author:** ![afabris\_supapass](https://avatars.discourse-cdn.com/v4/letter/a/5e9695/32.png) [@afabris\_supapass](https://discourse.hibernate.org/u/afabris_supapass)\
**Post date:** [February 3, 2019, 10:24pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/10 "2019-02-03T22:24:56Z")

</div>

Thanks a lot for all this explanation!  
Very useful

---

<div class="post-metadata">

**Author:** ![NeilS](https://avatars.discourse-cdn.com/v4/letter/n/3d9bf3/32.png) [@NeilS](https://discourse.hibernate.org/u/NeilS)\
**Post date:** [February 13, 2019, 3:54pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/11 "2019-02-13T15:54:51Z")

</div>

> [@vlad](#):
>
> > But why it includes in a select an associated table (child) even if no change was made on it in the code and it’s lazy loaded.
> 
> Because it needs to load the entire graph in order to detect changes that need to be propagated via Cascade. If you don’t want the JOIN, you either have to call `persist` or remove the `Cascade` .

Thanks.  
Why does it need to add that JOIN if the value of `Set<Child> children` is null? In that case there’s nothing to cascade.

---

<div class="post-metadata">

**Author:** ![vlad](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/vlad/32/658_2.png) [@vlad](https://discourse.hibernate.org/u/vlad)\
**Post date:** [February 13, 2019, 4:24pm UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/12 "2019-02-13T16:24:00Z")

</div>

> Why does it need to add that JOIN if the value of `Set<Child> children` is null? In that case there’s nothing to cascade.

First of all, using `null` for collections is a bad idea.

So, instead of:

```auto
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Child> children;

```

You should always have:

```auto
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Child> children = new HashSet<>();

```

Second, even if you set the `Set` to `null`, that would only be taken into account for [transient entities](https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/). Hibernate will have to fetch that association when the association is accessed, so it’s either a Proxy or a fully-initialized collection, but never `null`.

---

<div class="post-metadata">

**Author:** ![rhamedy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/rhamedy/32/859_2.png) [@rhamedy](https://discourse.hibernate.org/u/rhamedy)\
**Post date:** [November 24, 2019, 3:19am UTC](https://discourse.hibernate.org/t/onetomany-association-with-lazy-loading-and-cascade-all/2185/13 "2019-11-24T03:19:04Z")

</div>

Isn’t the default fetch strategy for `@OneToMany` and `@ManyToMany` already `FetchType.LAZY`? If yes, then the following should work as well

```auto
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private Set<Child> children = new HashSet<>();

```
