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);