I have these entities:
@Entity @Data @NoArgsConstructor public class Hero { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @NotNull private String name; @OneToMany(mappedBy = "character", cascade = CascadeType.ALL, orphanRemoval = true) @NotEmpty private List<HeroSkill> heroSkills; } @Entity @Data @NoArgsConstructor public class Skill { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(nullable = false) private String name; } @Entity @Data @NoArgsConstructor public class HeroSkill { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @ManyToOne @JoinColumn(name = "hero_id", referencedColumnName = "id") @JsonIgnore private Hero hero; @ManyToOne @JoinColumn(name = "skill_id", nullable = false) private Skill skill; private int ranks; }
My HeroService is like this:
@Transactional public void create(Hero hero) { heroRepository.save(hero); }
I am using postman to create a new Hero and this is a sample POST request:
{ "name": "Test", "heroSkills": [ { "skill": { "id": 1 }, "ranks": 4 }, { "skill": { "id": 2 }, "ranks": 4 } ] }
Although a hero is created and two entities on HeroSkill table are also created, the HeroSkill hero_id column is null, so my new heroes are not associated with their skills as they should be.
It works if I modify my service like this:
@Transactional public void save(Hero hero) { Hero result = heroRepository.save(hero); hero.getHeroSkills().stream() .forEach(it -> { it.setHero(result); heroSkillRepository.save(it); }); }
But I think that I shouldn’t have to pass the Hero manually to each HeroSkill. Isn’t that the point of adding CascateType.ALL (which includes CascadeType.PERSIST)?
What is the correct approach to that?
Thanks in advance.
Advertisement
Answer
There is no reference of Hero
(Parent) in HeroSkill
(child) that’s why JPA can’t resolve hero_id
and set as null. Use this heroSkill.setHero(hero)
to set parent in child.
To save child with parent in bidirectional relation you have to make sync both side.
hero.getHeroSkills().stream() .forEach(it -> { it.setHero(hero); }); Hero result = heroRepository.save(hero);