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 对象。

感谢大家的帮助!