Java JPA/Eclipselink如何处理循环/循环关系
我有一个关于JPA中循环关系的问题,这里特别是关于Eclipselink JPA实现的问题。对不起,如果问题有点长,我会尽量精确 让我们以部门和员工为例,其中一个部门具有一对多“员工”关系(因此员工与部门之间的多对一“部门”关系是相反的)。现在,让我们添加一个从部门到员工的一对一关系“经理”(部门的一名员工是同一部门的经理)。这将在两个实体之间引入循环关系,并且两个表都将有一个外键引用另一个表 我希望能够在不违反外键约束的情况下执行所有插入。因此,我的想法是首先插入所有员工(不设置部门关系),然后插入部门(设置其经理),最后更新所有员工以设置其部门 我知道我可以使用Java JPA/Eclipselink如何处理循环/循环关系,java,jpa,eclipselink,Java,Jpa,Eclipselink,我有一个关于JPA中循环关系的问题,这里特别是关于Eclipselink JPA实现的问题。对不起,如果问题有点长,我会尽量精确 让我们以部门和员工为例,其中一个部门具有一对多“员工”关系(因此员工与部门之间的多对一“部门”关系是相反的)。现在,让我们添加一个从部门到员工的一对一关系“经理”(部门的一名员工是同一部门的经理)。这将在两个实体之间引入循环关系,并且两个表都将有一个外键引用另一个表 我希望能够在不违反外键约束的情况下执行所有插入。因此,我的想法是首先插入所有员工(不设置部门关系),然
flush()
强制执行插入顺序,但有人告诉我应该避免这样做,因此我想知道是否有办法告诉JPA/Eclipselink应该先插入部门,然后再插入员工
在Eclipselink中,我尝试将Employee添加为Department类的classdescriptor的约束依赖项,但它仍然随机给出错误
下面是一个代码示例,说明了这一点(问题随机出现):
系级:
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;
}
}
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>
my.jpa.test.Department
my.jpa.test.Employee
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>
org.eclipse.persistence
日食
2.5.1
com.h2数据库
氢
1.3.172
我认为,如果将Employee中的department列配置为允许null并正确设置级联,则可以解决问题。请不要使用flush,因为您没有选择权
- 插入部门(无经理)
- 插入员工(与部门一起)
- 冲洗
- 更新部门经理
你真的需要oneToOne是双向的吗?@Gab,可能不需要,但我认为如果我将其从员工实体中删除,它不会改变任何东西。通过在那里定义它,我们只增强了JPA的元模型,但由于它没有强制外键约束,Eclipselink在插入顺序计算时不会考虑它(据我所知)。只需在关系上使用
CascadeType。持久化,就可以了。为什么告诉你不要使用flush?如果希望首先插入部门,确保插入顺序的唯一方法是持久化部门,然后调用flush,然后修复引用并持久化员工。当EclipseLink确定存在循环引用时,它将执行浅插入,并在插入后在单独的语句中更新FKs,因此大多数数据库上的另一个解决方案是将约束检查延迟到事务结束。通过这种方式,JPA/EclipseLink可以按任何顺序插入,并且只有在设置完所有内容后才检查约束。@SvetlinZarev使用CascadeType.PERSIST
将无法解决问题。使用cascade是一种简单的快捷方式,可以在对象图上执行操作,而不是对图中的每个对象执行一次操作。您仍然不知道插入的顺序。默认情况下,manyTone可为空/可选。我看不出设置级联有什么帮助。据我所知,cascade只是一个shorcut,它避免了对给定对象图的所有对象多次调用相同的方法
<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>