Q:

Why is id generated for local variable only when flush is made before save?

Lets say I have three classes.
public class Parent {
    @Id
    @GeneratedValue
    @Column(name = "parent_id", updatable = false, insertable = false)
    private Long id;

    private String name;

    @Builder.Default
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    Set<Child> childs = new HashSet<>();
}

public class Child {
    @Id
    @GeneratedValue
    @Column(name = "child_id", updatable = false, insertable = false)
    private Long id;

    private String name;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name ="parent_id")
    Parent parent;

    @Builder.Default
    @OneToMany(mappedBy = "child", cascade = CascadeType.ALL)
    Set<GrandChild> grandChildren = new HashSet<>();
}

public class GrandChild {
    @Id
    @GeneratedValue
    @Column(name = "grandchildid", updatable = false, insertable = false)
    private Long id;

    private String name;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "child_id")
    Child child;
}

If I then create a new GrandChild grandChild via parent.getChilds().get(0).getGranChilds().add(grandChild) and save the entity via parentRepo.save(parent). The local variable grandChild will not have a id. But if I first flush and then save, the local variable will have a id? Is this something you normally would do? 
I tought this would happend when you save and then flush. But seems only to be working when i FIRST flush then save.
Adding the grandChild:
   GrandChild grandChild = GrandChild.builder().name(name).build();
   Parent parent = parentRepo.findAll().get(0);

   Optional<Child> optional = parent.getChilds().stream().findAny();

   optional.ifPresent(child -> child.getGrandChildren().add(grandChild));

(This is just code for the example, so dont mind the ugliness). 
Saving with flush after (no id generated for the local variable, entity is still saved):
parentRepo.saveAndFlush(parent);
System.out.println(grindChild.getId()); // null

Flush before (local variable gets the id):
parentRepo.flush();
parentRepo.save(parent);
System.out.println(grandChild.getId()); //not null, value exists

The ID is, in both cases, generated in the database.

A:

The problem here was that different cascades action was made based on if a flush was made before or after the save. 
If I flush before I save, the entity will be saved via the ACTION_PERSIST_ON_FLUSH cascade which uses the same object reference as the local variable.
If I flush after I save, the entity will be saved via the ACTION_MERGE cascade which does a copy on the entity before the ID is generated, thus a different reference which means the local variable will not have a ID. 
In both cases saveWithGeneratedId(...) was called in org.hibernate.event.internal.AbstractSaveEventListener class. Just with different object references. 

