JPA / Eclipselink, как обрабатывать циклические / циклические отношения

1

У меня есть вопрос относительно круговых отношений в JPA, и здесь, в частности, с реализацией Eclipselink JPA. Извините, если вопрос немного длинный, но я стараюсь быть максимально точным.

Возьмем простой пример Департамента и Сотрудника, в котором у Департамента есть взаимоотношения "один-ко-многим" с сотрудниками (и, следовательно, обратное отношение "отдел" от Работника к Департаменту). Теперь добавьте индивидуальный "менеджер" от Департамента к Сотруднику (один из сотрудников Департамента является менеджером того же Департамента). Это вводит круговую взаимосвязь между двумя объектами, и обе таблицы будут иметь внешний ключ, ссылающийся на другую таблицу.

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

Я знаю, что я мог бы использовать flush() чтобы заставить порядок выполнения вставки, но мне сказали, что его следует избегать, и поэтому задается вопросом, есть ли способ сообщить JPA/Eclipselink, что Департамент должен быть вставлен первым, а затем Employee.

В Eclipselink я попытался добавить Employee в зависимость от ограничения класса classdescriptor класса Department, но он по-прежнему дает ошибку случайным образом.

Вот пример кода, иллюстрирующий это (проблема возникает случайным образом):

Класс отдела:

package my.jpa.test;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Persistence;

/**
 * Entity implementation class for Entity: Department
 *
 */
@Entity
public class Department implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(fetch = FetchType.EAGER)
    private List<Employee> employees;

    @OneToOne
    @JoinColumn(name = "manager", nullable = false)
    private Employee manager;

    private static final long serialVersionUID = 1L;

    public Department() {
        super();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-jpa");
        EntityManager em = emf.createEntityManager();
        Department d = new Department();
        Employee manager = new Employee();
        manager.setLastName("Doe");
        d.setManager(manager);
        Employee e1 = new Employee();
        e1.setLastName("Doe");
        Employee e2 = new Employee();
        e2.setLastName("Smith");
        em.getTransaction().begin();
        em.persist(d);
        manager.setDepartment(d);
        e1.setDepartment(d);
        e2.setDepartment(d);
        em.persist(e1);
        em.persist(e2);
        em.persist(manager);
        em.persist(d);
        manager.setDepartment(d);
        e1.setDepartment(d);
        e2.setDepartment(d);
        em.merge(manager);
        em.merge(e1);
        em.merge(e2);
        em.getTransaction().commit();
        em.clear();
        Department fetchedDepartment = em.find(Department.class, d.getId());
        System.err.println(fetchedDepartment.getManager().getLastName());
        System.err.println(new ArrayList<Employee>(fetchedDepartment.getEmployees()));
    }

    public Employee getManager() {
        return manager;
    }

    public void setManager(Employee manager) {
        this.manager = manager;
    }
}

Класс сотрудника:

package my.jpa.test;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;

/**
 * Entity implementation class for Entity: Employee
 *
 */
@Entity
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String lastName;

    @ManyToOne
    private Department department;

    @OneToOne(mappedBy = "manager")
    private Department managedDepartment;

    public Employee() {
        super();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public Department getManagedDepartment() {
        return managedDepartment;
    }

    public void setManagedDepartment(Department managedDepartment) {
        this.managedDepartment = managedDepartment;
    }

    @Override
    public String toString() {
        return "Employee " + getLastName();
    }

}

persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="test-jpa">
        <class>my.jpa.test.Department</class>
        <class>my.jpa.test.Employee</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE" />
            <property name="javax.persistence.jdbc.user" value="sa" />
            <property name="javax.persistence.jdbc.password" value="" />
            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
            <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
            <property name="eclipselink.ddl-generation.output-mode" value="database" />
            <property name="eclipselink.logging.level" value="FINE"/>
            <property name="eclipselink.logging.parameters" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

Зависимости Maven:

<dependencies>
    <dependency>
        <groupId>org.eclipse.persistence</groupId>
        <artifactId>eclipselink</artifactId>
        <version>2.5.1</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.3.172</version>
    </dependency>
</dependencies>
  • 0
    вам действительно нужно, чтобы oneToOne был двунаправленным?
  • 0
    @Gab, возможно, нет, но я не думаю, что это что-то изменит, если я удалю его из сущности Employee. Определив его там, мы только улучшаем метамодель JPA, но, поскольку она не применяет ограничение внешнего ключа, Eclipselink не будет учитывать его при вычислении порядка вставки (насколько я понимаю EL).
Показать ещё 4 комментария
Теги:
jpa
eclipselink

2 ответа

1

ИМХО с этой моделью у вас действительно нет выбора.

  • Вставить отдел (без менеджера)
  • вставить сотрудника (с отделами)
  • промывать
  • менеджер отдела обновлений.

Удаление, вероятно, будет беспорядок тоже

В противном случае вы можете создать таблицу ассоциаций между отделом и сотрудником для хранения атрибута isManager.

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

С общей точки зрения кажется, что круговая ссылка не рекомендуется в реляционной модели: в SQL, нормально ли для двух таблиц ссылаться друг на друга?

-1

Я думаю, что если вы сконфигурируете столбец отделов в Employee, чтобы разрешить нулевой и правильно установить каскады, он может решить проблему. И, пожалуйста, не используйте флеш

  • 0
    По умолчанию ManyToOne имеет значение null / необязательно. И я не вижу, как поможет установка каскада. Насколько я понимаю каскад, это просто сокращение, позволяющее избежать необходимости вызывать несколько раз один и тот же метод для всех объектов данного графа объектов.
  • 0
    иногда у вас нет выбора, и imho здесь дело обстоит именно так

Ещё вопросы

Сообщество Overcoder
Наверх
Меню