我目前的设置类似于:
@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约束所以当运行提交时,我得到:
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表中物理添加ONDELETE CASCADE时,而不是依赖hibernate来级联,一切都会正常工作。但这种方法很容易出错,因为如果在某个时候我决定对我的任何细节实体使用拦截器或eventListener,它根本不起作用,所以我更喜欢允许hibernate来处理它,而不需要依赖手动更改数据库结构。
您需要使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的映射,尽管如果每个类使用一个表,则会以额外的联接为代价。