如何使用Hibernate级联子删除?

时间:2015-02-25 14:47:52

标签: java hibernate jpa foreign-keys hibernate-cascade

我目前的设置类似于:

@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
    private Long id;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

@Entity
public class Container extends AbstractEntity {
    private Collection<Item> items = new HashSet<>();

    @Cascade(CascadeType.SAVE_UPDATE)
    @OneToMany(mappedBy = "container", orphanRemoval = true)
    public Collection<Item> getItems() { return items; }

    public void setItems(Collection<Item> items) { this.items = items; }
}

@Entity
public class Item extends AbstractEntity {
    private Container container;

    @ManyToOne(optional = false)
    public Container getContainer() { return container; }

    public void setContainer(Container container) { this.container = container; }
}

@Entity
public class FirstItemDetails extends AbstractEntity {
    private Item item;

    @OneToOne(optional = false)
    @Cascade({CascadeType.DELETE, CascadeType.REMOVE})
    public Item getItem() { return item; }

    public void setItem(Item item) { this.item = item; }
}

@Entity
public class SecondItemDetails extends AbstractEntity {
    private Item item;

    @OneToOne(optional = false)
    @Cascade({CascadeType.DELETE, CascadeType.REMOVE})
    public Item getItem() { return item; }

    public void setItem(Item item) { this.item = item; }
}

我遗漏了一些不必要的字段,因为它们最终没有任何区别。现在为了这个问题。 我想做的是:

public void removeItemFromContainer(Item item, Container container) {
    Transaction transaction = session.beginTransaction();
    container.getItems().remove(item);
    session.save(container);
    transaction.commit();
}

我对此函数的期望是从给定容器中物理删除项目,因为我将 orphanRemoval 设置为 true ,当我持久保存我的容器时,它实际上是尝试从数据库中删除该项目。这里的问题在于 ItemDetails实体,它有一个 foreign_key约束所以当运行 commit 时我得到:

org.hibernate.exception.ConstraintViolationException: could not execute statement
Caused by: java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no action; FK_FITEM_DETAILS_ITEM table: first_item_details
Caused by: org.hsqldb.HsqlException: integrity constraint violation: foreign key no action; FK_FITEM_DETAILS_ITEM table: first_item_details

对我来说最令人困惑的部分是,当我在数据库中的 first_item_details 表中物理添加 ON DELETE CASCADE 而不是依靠hibernate为我级联时,一切正常。但是这种方法很容易出错,因为如果在某些时候我决定使用拦截器 eventListener 来处理我的详细信息它只是简单地赢得了工作,所以我更喜欢允许hibernate处理它,而不是需要依赖手动的数据库结构更改。

1 个答案:

答案 0 :(得分:1)

您需要双向建立从Item到ItemDetails的关系。如果Item对此关系一无所知,为什么会删除相应的ItemDetail?

@Entity
public class Item extends AbstractEntity {
    private Container container;

    @OneToOne(mappedBy="item")
    @Cascade({CascadeType.DELETE, CascadeType.REMOVE})
    public ItemDetails itemDetails;
}

在此处查看类似的解答:

https://stackoverflow.com/a/7200221/1356423

虽然您注意到了一些问题,但您仍然可以通过使用JPA继承来实现此目的,这样Item仍然可以与ItemDetails建立单一关系。继承策略取决于您的数据库结构:

http://en.wikibooks.org/wiki/Java_Persistence/Inheritance

您的更新代码将如下所示:

@Entity
@Inheritance(/*defineStrategy*/)
//define discriminator column if required
public abstract class ItemDetails extends AbstractEntity {
    private Item item;

    @OneToOne(optional = false)
    @Cascade({CascadeType.DELETE, CascadeType.REMOVE})
    public Item getItem() { return item; }

    public void setItem(Item item) { this.item = item; }
}

@Entity
//define table or discriminator depending on strategy
public class FirstItemDetails extends ItemDetails {
    //map fields specific to thus sub-class
}

@Entity
//define table or discriminator depending on strategy
public class SecondItemDetails extends ItemDetails {
     //map fields specific to thus sub-class 
}

这样做的好处是不必在每个ItemDetails类中重复映射到Item,但如果你每个类使用一个表,它将以额外的连接为代价。