jpa级联删除反向关系

时间:2015-01-28 22:51:45

标签: java jpa eclipselink cascading

我的问题是关于使用JPA和Eclipselink级联删除。

我想模拟两个实体之间的简单关系:A和B. B通过属性引用A ref2a (在数据库术语 B.ref2a 中连接到 A.id 通过带有“ON DELETE CASCADE”的外键)。 我的目标是删除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();
        }

    }
}

2 个答案:

答案 0 :(得分:1)

我已经解决了我的问题。真的很简单 - 我的初始代码几乎是正确的。我只是在外键级联中遇到了语法问题。需要在括号“()”中的属性,我在文档中忽略了这一点。 所以我需要做的改变是:

@OneToOne
    @JoinColumn(
            foreignKey=@ForeignKey(
                    foreignKeyDefinition="FOREIGN KEY (ref2a) REFERENCES A (id) ON DELETE CASCADE"
                    )
            )
    private A ref2a;

请注意两个属性的括号。

这样做,删除A对象也会级联其链接的B对象。

感谢大家的帮助!

答案 1 :(得分:-1)

EclipseLink提供了一个与数据库" ON DELETE CASCADE"对齐的@CascadeOnDelete注释。 contraint。此注释告诉EclipseLink,当删除此实体时,实体将被数据库foriegn键约束删除,如果使用DDL,EclipseLink将生成具有适当约束的表。 有关详细信息,请参阅https://wiki.eclipse.org/EclipseLink/Examples/JPA/DeleteCascade

我认为你可以在FriendshipRelation.person映射上使用简单的级联删除:

@Entity
public class FriendshipRelation {
..
  @OneToOne(cascade=CascadeType.REMOVE)
  private Person person;

这将强制JPA在删除FriendshipRelation实例时删除任何被引用的人。