Spring JPA how to persist parent when the child is persisted in ManyToMany relationship

Moussa :

I have a basic project with a many to many relationship. There are 2 classes Parent and Child, the owner of the relationship is the class Parent. When parent entities are persisted child entities are also persisted (which is the desired behavior). But at the opposite when the child entities are persisted, the parent entities are not persisted.

How do I get the parent entities persisted at the same time than the child entities? The code below give the spring-boot class that allow to reproduce the issue, after the logger.info("---> 2nd fetch all parents"); I expect to have 5 parents but I have only two.

Here are my entity classes :

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    // @JoinTable => owner of the relationship
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "parent_child",
            joinColumns = @JoinColumn(name = "parent_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "child_id", referencedColumnName = "id"))
    private Set<Child> children;
}

@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    @ManyToMany(mappedBy = "children")
    private Set<Parent> parents;

    // getters and setters
}

The repositories

public interface ChildRepository extends JpaRepository<Child, Long> {}
public interface ParentRepository extends JpaRepository<Parent, Integer> {}

The springboot application

@SpringBootApplication
public class Application implements CommandLineRunner {
    private static final Logger logger = LoggerFactory.getLogger(Application.class);

    @Autowired
    private ParentRepository parentRepository;

    @Autowired
    private ChildRepository childRepository;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    @Transactional
    public void run(String... strings) throws Exception {
        // save a couple of parents
        Child childA = new Child("Child A"); Child childB = new Child("Child B"); Child childC = new Child("Child C");
        Parent parentA = new Parent("Parent A", new HashSet<>(Arrays.asList(childA, childB))); Parent parentB = new Parent("Parent B", new HashSet<>(Arrays.asList(childA, childC)));
        parentRepository.saveAll(Arrays.asList(parentA, parentB));

        // fetch all parents
        logger.info("---> 1st fetch all parents");
        for (Parent parent : parentRepository.findAll()) {
            logger.info(parent.toString());
        }

        // save a couple of children
        Parent parentD = new Parent("Parent D"); Parent parentE = new Parent("Parent E"); Parent parentF = new Parent("Parent F");
        Child childD = new Child("Child D", new HashSet<Parent>(Arrays.asList(parentD, parentE))); Child childE = new Child("Child E", new HashSet<Parent>(Arrays.asList(parentE, parentF)));
        childRepository.saveAll(Arrays.asList(childD, childE));

        // fetch all children
        logger.info("---> 1st fetch all children");
        for (Child child : childRepository.findAll()) {
            logger.info(child.toString());
        }

        // fetch all parents
        logger.info("---> 2nd fetch all parents");
        for (Parent parent : parentRepository.findAll()) {
            logger.info(parent.toString());
        }

        // fetch all children
        logger.info("---> 2nd fetch all children");
        for (Child child : childRepository.findAll()) {
            logger.info(child.toString());
        }
    }
}
bdzzaid :

With JPA when you want to propagated an update to a relational item you have to specify the type of propagation you want to apply.

So when you define a relation ManyToMany you can add the cascade type "PERSIST" to propagate the instruction INSERT for the entity Parent

@ManyToMany(mappedBy = "children", cascade = CascadeType.PERSIST)
private Set<Parent> parents;

Here the complete specification for Hibernate used by Springboot

In case you are using annotatons you probably have noticed the cascade attribute taking an array of CascadeType as a value. The cascade concept in JPA is very is similar to the transitive persistence and cascading of operations as described above, but with slightly different semantics and cascading types:

  • CascadeType.PERSIST : cascades the persist (create) operation to associated entities persist() is called or if the entity is managed
  • CascadeType.MERGE : cascades the merge operation to associated entities if merge() is called or if the entity is managed
  • CascadeType.REMOVE : cascades the remove operation to associated entities if delete() is called
  • CascadeType.REFRESH : cascades the refresh operation to associated entities if refresh() is called
  • CascadeType.DETACH : cascades the detach operation to associated entities if detach() is called CascadeType.ALL: all of the above

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=145960&siteId=1