外键级联删除在OpenJPA / Postgresql中无法正常工作

时间:2016-02-18 23:34:21

标签: java postgresql jpa jboss-arquillian tomee

有人可以解释为什么我没有看到OpenJPA的@ForeignKey注释要求Cascade Delete的预期结果吗?

我已将此缩减为一个小型测试用例,其中包含两个JPA实体类" Parent"和"孩子"分别由OneToMany和ManyToOne关系相关。 Child与Parent的关系使用OpenJPA @ForeignKey注释进行注释,指定删除应该级联。

映射和SchemaFactory设置设置为尊重外键。

数据库正确反映了外键关系。

但是,在测试用例中,当我删除父级时,子级不会删除。

的persistence.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- See this: http://forum.springsource.org/showthread.php?58527-transaction-type-quot-JTA-quot-quot-RESOURCE_LOCAL-quot -->

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="entitiesTestPersistenceUnit" transaction-type="JTA">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <jta-data-source>entitiesDsJtaTest</jta-data-source>
        <non-jta-data-source>entitiesDsNonJtaTest</non-jta-data-source>
        <validation-mode>AUTO</validation-mode>
        <properties>
         <property name="openjpa.jdbc.DBDictionary" value="org.apache.openjpa.jdbc.sql.PostgresDictionary"/>
            <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"  />
            <property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)"/> 
            <property name="openjpa.RuntimeUnenhancedClasses" value="unsupported" />
            <property name="openjpa.Log" value="slf4j" />
            <property name="openjpa.LockTimeout" value="5000" />
        </properties>
    </persistence-unit>
</persistence>

父类:

@Entity
public class Parent extends DbEntity {

    public Parent()  {
    }

    private String name;

    public void setName (String name) {
    this.name = name;
    }

    public String getName() {
    return this.name;
    }


    @OneToMany(mappedBy = "parent",  fetch=FetchType.LAZY)
    private Set<Child> children = new HashSet<>();

    public void addChild(Child child) {
        if (child.getParent() != this) {
            throw new IllegalStateException(
                    "Attempt to add a Child to a usage point that does not own it.");
        }
        this.getChildren().add(child);
    }

    public void removeChild(Child child) {
        if (child.getParent() != null) {
            throw new IllegalStateException(
                    "Attempt to remove a Child that stil belongs to a Parent.");
        }
        this.children.remove(child);
    }

    public boolean hasChild(Child child) {
        return this.children.contains(child);
    }

    public Set<Child> getChildren() {
        return this.children;
    }

}

儿童班:

@Entity
    public class Child extends DbEntity {

    public Child() {
    }

    private String name;

    public void setName(String name) {
    this.name = name;
    }

    public String getName() {
    return this.name;
    }


    @ManyToOne(fetch=FetchType.LAZY)
    @ForeignKey(deleteAction = ForeignKeyAction.CASCADE, updateAction = ForeignKeyAction.CASCADE)
    private Parent parent = null;


    public Parent getParent() {
    return this.parent;
    }

    public void setParent(Parent parent) {
        Parent old = this.getParent();
        if (parent != old) {
            if (old != null) {
                this.parent = null;
                old.removeChild(this);
            }
            this.parent = parent;
            if (parent != null) {
                parent.addChild(this);
            }
        }
    }

}

测试用例(用Arquillian运行)

@Test
public void testCascadeDeleteParent() throws IllegalStateException, SecurityException, HeuristicMixedException, HeuristicRollbackException, RollbackException, SystemException, NotSupportedException {
    Child child = new Child();
    em.persist(child);
    Parent parent = new Parent();
    em.persist(parent);
    child.setParent(parent);
    long id1 = child.getId();
    // Child is manaaged.
    Assert.assertNotNull("child has no ID?", id1); 
    long id2 = parent.getId();
    Assert.assertNotNull("parent has no ID?", id2);
    this.em.remove(parent);
    Parent p2 = this.em.find(Parent.class, id2);
    Assert.assertNull("Parent should be gone; we deleted it. ", p2);
    Child s1 = this.em.find(Child.class, id1);
    Assert.assertNull("Child should be gone after Parent is deleted.", s1);
}

数据库肯定有外键操作:

test=# \d+ dbentity;
                                Table "public.dbentity"
  Column   |          Type          | Modifiers | Storage  | Stats target | Description 
-----------+------------------------+-----------+----------+--------------+-------------
 id        | bigint                 | not null  | plain    |              | 
 version   | integer                |           | plain    |              | 
 dtype     | character varying(255) |           | extended |              | 
 name      | character varying(255) |           | extended |              | 
 parent_id | bigint                 |           | plain    |              | 
Indexes:
    "dbentity_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "dbentity_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES dbentity(id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
Referenced by:
    TABLE "dbentity" CONSTRAINT "dbentity_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES dbentity(id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
Has OIDs: no

test=# 

堆栈顶部的跟踪,无论它的价值如何:

   java.lang.AssertionError: Child should be gone after Parent is deleted. expected null, but was:<com.testco.Child@5bfcf845>
        at org.junit.Assert.fail(Assert.java:88)
        at org.junit.Assert.failNotNull(Assert.java:664)
        at org.junit.Assert.assertNull(Assert.java:646)
        at com.testco.ChildIntegrationTest.testCascadeDeleteParent(ChildIntegrationTest.java:138)

0 个答案:

没有答案