JPA: Вопрос о несоответствии импеданса в отношениях OneTomany
Вопрос
У меня вопрос о jpa-2.0 (поставщик - это гибернация) отношения и их соответствующее управление в Java. Давайте предположим, что у меня есть кафедра и субъект сотрудника:
@Entity
public class Department {
...
@OneToMany(mappedBy = "department")
private Set<Employee> employees = new HashSet<Employee>();
...
}
@Entity
public class Employee {
...
@ManyToOne(targetEntity = Department.class)
@JoinColumn
private Department department;
...
}
Теперь я знаю, что я должен управлять отношениями Java сам, как в следующем тесте подразделения:
@Transactional
@Test
public void testBoth() {
Department d = new Department();
Employee e = new Employee();
e.setDepartment(d);
d.getEmployees().add(e);
em.persist(d);
em.persist(e);
assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
assertNotNull(em.find(Department.class, d.getId()).getEmployees());
}
Если я уйду либо e.setDepartment(d)
или d.getEmployees().add(e)
Утверждения потерпят неудачу. Все идет нормально. Что, если я совершаю транзакцию базы данных между ними?
@Test
public void testBoth() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Department d = new Department();
Employee e = new Employee();
e.setDepartment(d);
d.getEmployees().add(e);
em.persist(d);
em.persist(e);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
assertNotNull(em.find(Department.class, d.getId()).getEmployees());
em.getTransaction().commit();
em.close();
}
Нужно ли мне управлять обе стороны отношения? Нет, как оказывается, мне не нужно. С этой модификацией
e.setDepartment(d);
//d.getEmployees().add(e);
Утверждения все еще добиваются успеха. Однако, если я устанавливаю только другую сторону:
//e.setDepartment(d);
d.getEmployees().add(e);
Утверждения терпят неудачу. Почему? Это потому, что работник - это собственная сторона отношения? Могу ли я изменить это поведение, аннотируя по-разному? Или это просто всегда «одна» сторона «OneTomany», которая определяет, когда полное поле внешнего ключа в базе данных заполнена?
Решение
Отношения сущности в JPA имеют владение и обратными сторонами. Обновления баз данных определяются состоянием владения. В твоем случае Employee
это собственная сторона из-за mappedBy
атрибут.
2,9 отношения сущности
...
Отношения могут быть двунаправленными или однонаправленными. Двунаправленные отношения имеют как владение, так и обратная (некурящая) сторона. У однонаправленных отношений есть только владение. Владая сторона отношений определяет обновления отношений в базе данных, как описано в разделе 3.2.4.
Следующие правила применяются к двунаправленным отношениям:
- Обратная сторона двунаправленных отношений должна сослаться на свою сторону, используя элемент MapPapty OneToone, OneTomany или Manytomany Annotation. Элемент Map Cappy обозначает свойство или поле в объекте, который является владельцем отношений.
- Многие сторона однозначного / много к одному двунаправленным отношениям должны быть владеть стороной, следовательно, элемент MapPaphy не может быть указан на аннотации MUNALTOONE.
- Для односторонних двунаправленных отношений, владение на стороне соответствует стороне, которая содержит соответствующий внешний ключ.
- Для многих к-многим двунаправленным отношениям обе стороны могут быть собственной стороной.
Другие советы
Я не знаю, что твой тест пытается продемонстрировать, но тот факт, что вы должен обрабатывать обе стороны ассоциации при работе с двунаправленными ассоциациями. Не делать это неверно. Период.
Обновлять: Хотя SPECT, упомянутая axtavt, конечно точно, я настаиваю, вы определенно должны установить обе стороны двунаправленной ассоциации. Не делает это неверно, а связь между вашими организациями в первом контексте настойчивости сломанный. Отказ То JPA Wiki Book ставит это так:
Как и во всех двунаправленных отношениях, это ваша объектная модель и ответственность заявок, чтобы поддерживать отношения в обоих направлениях. В JPA нет волшебства, если вы добавите или удалите на одну сторону коллекции, вы также должны добавить или удалить с другой стороны, см. Коррупция объекта. Отказ Технически база данных будет регулярно обновляться, если вы добавите / удалите только с собственной стороны отношений, но затем модель объекта будет вне синхронизации, что может вызвать проблемы.
Другими словами, единственный верный и сейф Способ управления вашей двунаправленной ассоциацией в Java - установить обе стороны ссылки. Обычно это делается с использованием методов управления оборонительными каналами, как это:
@Entity
public class Department {
...
@OneToMany(mappedBy = "department")
private Set<Employee> employees = new HashSet<Employee>();
...
public void addToEmployees(Employee employee) {
this.employees.add(employee);
employee.setDepartment(this);
}
}
Я повторяю, не делаю это неверно. Ваш тест работает только, потому что вы попадаете в базу данных в новом контексте настойчивости (то есть очень конкретная ситуация, а не общий), но код сломался во многих других ситуациях.
Причина, по которой второй тест в новом контексте настойчивости преуспевает, если вы обновляете только владение в предыдущем контексте, заключается в том, что поставщик постоянства, очевидно, не может знать, что при сохранении вы также не обновили обратную сторону. Это только заботится о владении целей настойчивости. Однако, когда вы получаете постоянные объекты из поставщика постоянства, поставщик правильно устанавливает двунаправленные ассоциации с обеих сторон (просто предполагается, что они тоже были сохранены правильно). Однако, так как многие другие здесь уже указали, это не ответственность поставщика настойчивости завершить вновь созданные двунаправленные ассоциации, и вы всегда должны правильно поддерживать двунаправленные ассоциации в вашем коде.