JPA: Вопрос о несоответствии импеданса в отношениях OneTomany

StackOverflow https://stackoverflow.com/questions/3630439

  •  26-09-2019
  •  | 
  •  

Вопрос

У меня вопрос о 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 атрибут.

От Спецификация JPA 2.0:

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);
    }
}

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

Причина, по которой второй тест в новом контексте настойчивости преуспевает, если вы обновляете только владение в предыдущем контексте, заключается в том, что поставщик постоянства, очевидно, не может знать, что при сохранении вы также не обновили обратную сторону. Это только заботится о владении целей настойчивости. Однако, когда вы получаете постоянные объекты из поставщика постоянства, поставщик правильно устанавливает двунаправленные ассоциации с обеих сторон (просто предполагается, что они тоже были сохранены правильно). Однако, так как многие другие здесь уже указали, это не ответственность поставщика настойчивости завершить вновь созданные двунаправленные ассоциации, и вы всегда должны правильно поддерживать двунаправленные ассоциации в вашем коде.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top