2015-06-09 15 views
1

Мне не хватает чего-то очень фундаментального. Ниже приведены два объекта Department (обратная сторона) и Employee (владеющая сторона), образующая отношение «один ко многим» от Department до Employee.Двунаправленные отношения в JPA

Department.java

@Entity 
@Table(catalog = "testdb", schema = "", uniqueConstraints = { 
    @UniqueConstraint(columnNames = {"department_id"})}) 
public class Department implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Basic(optional = false) 
    @Column(name = "department_id", nullable = false) 
    private Long departmentId; 

    @Column(name = "department_name", length = 255) 
    private String departmentName; 

    @Column(length = 255) 
    private String location; 

    @OneToMany(mappedBy = "department", fetch = FetchType.LAZY) 
    private List<Employee> employeeList = new ArrayList<Employee>(0); 

    private static final long serialVersionUID = 1L; 

    // Constructors + getters + setters + hashcode() + equals() + toString(). 
    // No defensive link (relationship) management methods have yet been added to. 
    // CascadeType is also kept at a distance for now. 
} 

Employee.java

@Entity 
@Table(catalog = "testdb", schema = "", uniqueConstraints = { 
    @UniqueConstraint(columnNames = {"employee_id"})}) 
public class Employee implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Basic(optional = false) 
    @Column(name = "employee_id", nullable = false) 
    private Long employeeId; 

    @Column(name = "employee_name", length = 255) 
    private String employeeName; 

    @JoinColumn(name = "department_id", referencedColumnName = "department_id") 
    @ManyToOne(fetch = FetchType.LAZY) 
    private Department department; 

    private static final long serialVersionUID = 1L; 

    // Constructors + getters + setters + hashcode() + equals() + toString(). 
    // No defensive link (relationship) management methods have yet been added to. 
    // CascadeType is also kept at a distance for now. 
} 

Ниже некоторые методы в безгосударственном EJB (с использованием СМТА) делают не проходит, объединять и удалять операции соответственно.

public List<Employee> persist() { 
    Employee employee = new Employee(); 
    employee.setEmployeeName("a"); 
    employee.setDepartment(entityManager.getReference(Department.class, 1L)); 
    entityManager.persist(employee); 
    return employee.getDepartment().getEmployeeList(); 
} 

public List<Employee> merge(Employee employee) { 
    employee.setEmployeeName("b"); 
    employee.setDepartment(entityManager.getReference(Department.class, 1L)); 
    return entityManager.merge(employee).getDepartment().getEmployeeList(); 
} 

public List<Employee> remove(Employee employee) { 
    entityManager.remove(entityManager.contains(employee) ? employee : entityManager.merge(employee)); 
    return entityManager.getReference(Employee.class, employee.getEmployeeId()).getDepartment().getEmployeeList(); 
} 

public Employee getEmployeeById(Long id) { 
    return entityManager.find(Employee.class, id); 
} 

Эти методы вызываются связанным клиентом приложения поочередно (один за другим) в не транзакционной среде.

List<Employee> persistedList = employeeSessionBean.persist(); 
for (Employee employee : persistedList) { 
    System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName()); 
} 

List<Employee> mergedList = employeeSessionBean.merge(employeeSessionBean.getEmployeeById(23L)); 
for (Employee employee : mergedList) { 
    System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName()); 
} 

List<Employee> listAfterRemoving = employeeSessionBean.remove(employeeSessionBean.getEmployeeById(23L)); 

for (Employee employee : listAfterRemoving) { 
    System.out.println(employee.getEmployeeId() + " : " + employee.getEmployeeName()); 
} 

В списке (List<Employee>) на обратной стороне отношений автоматически отражает правильное состояние во время каждой операции, как указано выше.

  • Когда Employee объекта сохраняются, она указана в списке на обратной сторона (я явно не добавляя новый сохранялось Employee сущности к List<Employee> на обратной стороне).
  • При объединении объекта Employee изменения, внесенные в объект, автоматически отражаются соответствующим субъектом в List<Employee> на обратной стороне (я не изменяю явным образом соответствующий объект, принадлежащий списку сотрудников (List<Employee>) на обратном боковая сторона).
  • Аналогично, когда объект Employee удален, он также удаляется из списка на обратной стороне отношения (я не , явно удаляющий этот объект из списка на обратной стороне).

Я в настоящее время на EcliseLink 2.6.0. Почему я вижу такое поведение, которое не соответствует следующему тексту?

Как и во всех двунаправленных отношениях это ваша объектная модель и ответственности приложения для поддержания отношений в обоих направлении. В JPA нет магии, если вы добавите или удалите одну сторону из коллекции, вам необходимо также добавить или удалить с другой стороны, см. object corruption. Технически база данных будет обновлена ​​ правильно, если вы только добавите/удалите с собственной стороны отношения , но тогда ваша объектная модель будет не синхронизирована, что может вызвать проблемы .

http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Bi-directional_Many_to_Many

+0

Ничего себе, я никогда не видел, чтобы кто-то жаловался на программное обеспечение, делающее ** больше **, чем они ожидали! – DuncanKinnear

ответ

3

Что это означает, что с вашим конкретным примером, если изменить код для добавления сотрудника в отдел (а не другой способ настройки отдела), то вы заметите, что это не будет автоматически устанавливать отдел на сотрудника. Вам нужно будет написать код, чтобы сделать это явно.

Итак, хотя конкретный путь кода, как вы показали, работает, это не значит, что вы можете положиться на него. Я могу угадать, почему это работает - коллекция загружается лениво, и поскольку объект сохраняется до загрузки коллекции, он может вытащить правильные данные из БД.

Лучшее решение - прислушаться к советам в документации и правильно установить состояние на обоих концах двунаправленной связи, несмотря на соображения производительности (это можно доработать позже).

+0

+1. Да, ленивая загрузка списков сотрудников, инициированных методом getEmployeeList(), вероятно, вызывает SQL Select сотрудников отдела, поэтому она будет отражать любые сделанные изменения. Включение SQL-регистрации, вероятно, подтвердит это. – DuncanKinnear

+0

Я не смог полностью понять часть в первом абзаце ». * Если вы изменили код, чтобы добавить сотрудника в отдел (а не другой способ настройки отдела), то вы заметите, что это не будет автоматически устанавливать отдел на рабочем месте. Вам нужно будет написать код, чтобы сделать это. * « – Tiny

+0

, т. е. если это делается вместо: entityManager.getReference (Department.class, 1L) .getEmployeeList(). add (employee) – codedabbler