Search code examples
javaspring-boothibernatehibernate-mapping

How persist two objects related to each other at the same time?


I am trying to persist a new JobAdvertise object with a list of new JobAdTasks objects. An advertise contains tasks related to it, both persisted successfully at the same time. However, the foreign key for each JobAdTask is empty. How can I solve this problem, persisting two objects at the same in a two-way mapping relationship?

JobAdvertise class:

@JsonManagedReference
@OneToMany(cascade = CascadeType.ALL,mappedBy = "jobAdvertise")
private List<JobAdTask> taskList = new ArrayList<>();

public void addTask(JobAdTask adTask) {
   if (adTask != null) {
      if (taskList == null) { taskList = new ArrayList<>(); }
      taskList.add(adTask);
   }
}

JobAdTask class:

@JsonBackReference
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
@JoinColumn(name = "job_add_id")
private JobAdvertise jobAdvertise;

JobAdvertiseService service class:

JobAdvertise jobAdvertise = new JobAdvertise();

for (String task : jobAdCreation.getTaskList()) {
   JobAdTask jobAdTask = new JobAdTask();
   jobAdTask.setTask(task);
   jobAdTaskRepo.save(jobAdTask);
   
   //Relationship JobAdvertise and JobAdTask
   jobAdvertise.addTask(jobAdTask);
}

jobAdvertiseRepo.save(jobAdvertise);

Solution

  • In your service, you need to change to the following:

    for (String task : jobAdCreation.getTaskList()) {
       JobAdTask jobAdTask = new JobAdTask();
       jobAdTask.setTask(task);
       jobAdTask.setJobAdvertise(jobAdvertise);
       jobAdTask = jobAdTaskRepo.save(jobAdTask); // Also re-assign the jodAdTask variable since otherwise the id and any timestamps managed by the ORM won't be available in your object.
       
       //Relationship JobAdvertise and JobAdTask
       jobAdvertise.addTask(jobAdTask);
    }
    

    An option would be to re-write the code as following:

    // in JobAdvertise
    public void addTask(JobAdTask adTask) {
       if (adTask != null) {
          adTask.setJobAdvertise(this); // Link the objects here
          if (taskList == null) { taskList = new ArrayList<>(); }
          taskList.add(adTask);
       }
    }
    
    public void addTasks(Collection<JobAdTask> adTasks) { // add this method to enable passing a collection to the object, removing the need for iteration from the service class.
       adTasks.forEach(this::addTask);
    }
    
    // in JobAdTask create a constructor taking a string
    
    public JobAdTask(String task) {
       this.setTask(task);
    }
    
    // in JobAdvertiseService
    List<JobAdTask> jobAdTasks = jobAdCreation.getTaskList().stream().map(JobAdTask::new).collect(Collectors.toList()); // creates list of jobAdTasks using the constructor taking a String argument
    
    JobAdvertise jobAdvertise = new JobAdvertise();
    jobAdvertise.addTasks(jobAdTasks); // the addTasks uses the addTask method of jobAdvertise, which links the JobAdvertise object and each JobAdTask object.
    jobAdvertise = jobAdvertiseRepo.save(jobAdvertise); // when you save the JobAdvertise, since you use CascadeType.ALL, both the advertise and each task object will be persisted to the database.