Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA - @OneToMany update

Consider that there are two entities, Department and Employee, where in one department there are N employees.

In Departament:

@OneToMany(mappedBy = "department", fetch = FetchType.EAGER)
private Collection<Employee> employees = new ArrayList<Employee>();

In Employee:

@ManyToOne(fetch = FetchType.EAGER)
private Department department;

Everything works, but I would like to add employees to the department without set the inverse relationship. For example:

// I will add two employees to a department
department.getEmployees().add(employee1);
department.getEmployees().add(employee2);

// In fact, it is necessary to set the opposite side of the relationship
employee1.setDepartment(department);
employee2.setDepartment(department);

entityManager.merge(department);      
//...

So, my question is: is there some way (eg. by some annotation) that the JPA will understand that it should propagate the changes to the other side of the relationship without I explicit that? In other words, I would like to do only this:

department.getEmployees().add(employee1);
department.getEmployees().add(employee2);
entityManager.merge(department);

Thanks a lot!

like image 817
joaosavio Avatar asked Dec 08 '11 17:12

joaosavio


2 Answers

The clear answer is: No, it is not possible that your JPA provider can automatically handle bidirectional relations they way you described it.

You can however implement the logic for setting bidirectional associations in your entities, maybe something along these lines:

class Department {

  public void addEmployee(Employee empl) {
    if (empl.getDepartment() != null && !this.equals(empl.getDepartment())) {
      empl.getDepartment().getEmployees().remove(empl);
    }
    empl.setDepartment(this); // use the plain setter without logic
    this.employees.add(empl);
  }
}


class Employee {
  // additional setter method with logic
  public void doSetDepartment(Department dept) {
    if (this.department != null && !this.department.equals(dept)) {
      this.department.getEmployees().remove(this);
    }
    dept.getEmployees().add(this);
    this.department = dept;
  }
}

In this case you must however ensure that the associations are already initialized when the entities are handled outside the persistence context to avoid lazy init exceptions. This might force you to switch to eager loading for all associations, which is generally not a good choice. Due to the complexity of bidirectional associations I personally avoid bidirectional associations in entities and only use them when there are really good reasons for it.

like image 197
tscho Avatar answered Sep 30 '22 15:09

tscho


JPA is not going to manage your java Object graph for you. You can either update the object graph yourself as you do in your question, or I guess you could probably reload all the entities after saving.

I'm not a fan of bidirectional relationships because they can get messy, but if you must do it then you will probably want to select one side to be the "owning" side of the relationship. Look for where it talks about "mappedBy" on this page http://www.objectdb.com/java/jpa/entity/fields for information on how to do that.

If you have a service that you have implemented, then you could provide a service call that takes care of managing this kind of stuff, then you won't have as great a chance of forgetting it in one place in your code and doing it correctly in the other 15 places.

like image 43
digitaljoel Avatar answered Sep 30 '22 15:09

digitaljoel