jpa级联删除反向关系
jpa cascading deletes reverse relationship
我的问题是关于使用 JPA 和 Eclipselink 进行级联删除。
我想模拟两个实体之间的简单关系:A 和 B。B 通过 属性 ref2a 引用 A(在数据库中 B.ref2a 通过带有 "ON DELETE CASCADE" 的外键连接到 A.id。
我的目标是在删除 A 对象时将删除级联到引用它的所有 B 对象。
我搜索了很多,但我无法使它工作。我发现的大多数解决方案都是针对相反的情况:A 包含对 B 的引用的集合。这就像一个魅力。但是如果引用在B端,我就不知道怎么办了。
这是代码示例:
@Entity
public class A
{
@Id
@GeneratedValue
private Integer id;
private String name;
// ...
}
@Entity
public class B
{
@Id
@GeneratedValue
private Integer id;
private String name;
@OneToOne
@JoinColumn(
foreignKey=@ForeignKey(
foreignKeyDefinition="FOREIGN KEY ref2a REFERENCES A id ON DELETE CASCADE"
)
)
private A ref2a;
// ...
}
以及测试代码:
public class CascadeTest extends TestCase
{
private EntityManagerFactory emf;
private EntityManager em;
@Override
protected void setUp() throws Exception {
emf = Persistence.createEntityManagerFactory("myDB");
em = emf.createEntityManager();
}
@Override
protected void tearDown() throws Exception {
em.close();
emf.close();
}
public void testApp()
{
Integer aid = -1, bid = -1;
try {
em.getTransaction().begin();
A a = new A();
a.setName("My name is A");
B b = new B();
b.setRef2a(a);
b.setName("My name is B, please delete me when A is gone.");
em.persist(a);
em.persist(b);
em.getTransaction().commit();
aid = a.getId();
bid = b.getId();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
try {
em.getTransaction().begin();
B b = em.find(B.class, bid);
assertNotNull(b);
assertEquals("My name is B, please delete me when A is gone.", b.getName());
assertEquals("My name is A", b.getRef2a().getName());
assertEquals(aid, b.getRef2a().getId());
A a = em.find(A.class, aid);
assertEquals("My name is A", a.getName());
em.remove(a);
em.getTransaction().commit();
em.getTransaction().begin();
// a should have been removed.
// This passes OK.
a = em.find(A.class, aid);
assertNull(a);
// Cascading deletes should have deleted also b.
b = em.find(B.class, bid);
// PROBLEM: This fails - b is still here.
assertNull(b);
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
}
}
EclipseLink 提供了与数据库 "ON DELETE CASCADE" 约束一致的 @CascadeOnDelete 注释。这个注释告诉 EclipseLink,当这个实体被删除时,该实体将被数据库外键约束删除,如果使用 DDL,EclipseLink 将生成具有适当约束的 table。
有关详细信息,请参阅 https://wiki.eclipse.org/EclipseLink/Examples/JPA/DeleteCascade。
我认为您可以通过 FriendshipRelation.person 映射上的简单级联删除来解决问题:
@Entity
public class FriendshipRelation {
..
@OneToOne(cascade=CascadeType.REMOVE)
private Person person;
这将强制 JPA 在删除 FriendshipRelation 实例时删除任何引用的人。
我的问题已经解决了。真的非常简单——我的初始代码几乎是正确的。我刚刚在外键级联中遇到语法问题。属性需要放在方括号“()”中,我在文档中忽略了这一点。
所以我需要做的改变是:
@OneToOne
@JoinColumn(
foreignKey=@ForeignKey(
foreignKeyDefinition="FOREIGN KEY (ref2a) REFERENCES A (id) ON DELETE CASCADE"
)
)
private A ref2a;
请注意两个属性两边的括号。
这有效,删除 A 对象也会级联其链接的 B 对象。
感谢大家的帮助!
我的问题是关于使用 JPA 和 Eclipselink 进行级联删除。
我想模拟两个实体之间的简单关系:A 和 B。B 通过 属性 ref2a 引用 A(在数据库中 B.ref2a 通过带有 "ON DELETE CASCADE" 的外键连接到 A.id。 我的目标是在删除 A 对象时将删除级联到引用它的所有 B 对象。
我搜索了很多,但我无法使它工作。我发现的大多数解决方案都是针对相反的情况:A 包含对 B 的引用的集合。这就像一个魅力。但是如果引用在B端,我就不知道怎么办了。
这是代码示例:
@Entity
public class A
{
@Id
@GeneratedValue
private Integer id;
private String name;
// ...
}
@Entity
public class B
{
@Id
@GeneratedValue
private Integer id;
private String name;
@OneToOne
@JoinColumn(
foreignKey=@ForeignKey(
foreignKeyDefinition="FOREIGN KEY ref2a REFERENCES A id ON DELETE CASCADE"
)
)
private A ref2a;
// ...
}
以及测试代码:
public class CascadeTest extends TestCase
{
private EntityManagerFactory emf;
private EntityManager em;
@Override
protected void setUp() throws Exception {
emf = Persistence.createEntityManagerFactory("myDB");
em = emf.createEntityManager();
}
@Override
protected void tearDown() throws Exception {
em.close();
emf.close();
}
public void testApp()
{
Integer aid = -1, bid = -1;
try {
em.getTransaction().begin();
A a = new A();
a.setName("My name is A");
B b = new B();
b.setRef2a(a);
b.setName("My name is B, please delete me when A is gone.");
em.persist(a);
em.persist(b);
em.getTransaction().commit();
aid = a.getId();
bid = b.getId();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
try {
em.getTransaction().begin();
B b = em.find(B.class, bid);
assertNotNull(b);
assertEquals("My name is B, please delete me when A is gone.", b.getName());
assertEquals("My name is A", b.getRef2a().getName());
assertEquals(aid, b.getRef2a().getId());
A a = em.find(A.class, aid);
assertEquals("My name is A", a.getName());
em.remove(a);
em.getTransaction().commit();
em.getTransaction().begin();
// a should have been removed.
// This passes OK.
a = em.find(A.class, aid);
assertNull(a);
// Cascading deletes should have deleted also b.
b = em.find(B.class, bid);
// PROBLEM: This fails - b is still here.
assertNull(b);
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
}
}
EclipseLink 提供了与数据库 "ON DELETE CASCADE" 约束一致的 @CascadeOnDelete 注释。这个注释告诉 EclipseLink,当这个实体被删除时,该实体将被数据库外键约束删除,如果使用 DDL,EclipseLink 将生成具有适当约束的 table。 有关详细信息,请参阅 https://wiki.eclipse.org/EclipseLink/Examples/JPA/DeleteCascade。
我认为您可以通过 FriendshipRelation.person 映射上的简单级联删除来解决问题:
@Entity
public class FriendshipRelation {
..
@OneToOne(cascade=CascadeType.REMOVE)
private Person person;
这将强制 JPA 在删除 FriendshipRelation 实例时删除任何引用的人。
我的问题已经解决了。真的非常简单——我的初始代码几乎是正确的。我刚刚在外键级联中遇到语法问题。属性需要放在方括号“()”中,我在文档中忽略了这一点。 所以我需要做的改变是:
@OneToOne
@JoinColumn(
foreignKey=@ForeignKey(
foreignKeyDefinition="FOREIGN KEY (ref2a) REFERENCES A (id) ON DELETE CASCADE"
)
)
private A ref2a;
请注意两个属性两边的括号。
这有效,删除 A 对象也会级联其链接的 B 对象。
感谢大家的帮助!