JPA/Hibernate 级联删除不工作
JPA/Hibernate cascade remove not working
我有这些实体:
@Entity
public class Item extends Unit
{
// @Id is in superclass
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<ItemRelation> lowerItemRelations = new LinkedHashSet<>();
@OneToMany(mappedBy = "child", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<ItemRelation> higherItemRelations = new LinkedHashSet<>();
// this too is in superclass
@OneToMany(mappedBy = "unit", cascade = CascadeType.REMOVE, orphanRemoval = true)
@OrderBy("date")
protected Set<UnitRegistration> registrations = new LinkedHashSet<>();
...
}
@Entity
@Table(name = "ITEM_RELATION",
indexes = @Index(columnList = "PARENT_ID, CHILD_ID", unique = true))
public class ItemRelation extends AbstractEntity
{
// @Id is in superclass
@ManyToOne(optional = false)
@JoinColumn(name = "PARENT_ID")
private Item parent;
@ManyToOne(optional = false)
@JoinColumn(name = "CHILD_ID")
private Item child;
@NotNull
@Min(0)
@Column(nullable = false, columnDefinition = "INT DEFAULT 1 NOT NULL")
private int quantity = 1;
...
}
现在我只想执行一个简单的em.remove(item)
,但是Hibernate不会为lowerItemRelations
/higherItemRelations
.
发出相关的DELETE
语句
相反,对于用 @OneToMany(mappedBy = "...", cascade = CascadeType.ALL/REMOVE, orphanRemoval=true)
注释的所有其他字段,它会发出语句。
这里有一小段 MySQL 日志片段:
2016-09-28T08:47:52.090453Z 13 Query update UNIT set CODE='CE13000003167', ... where ID=132241 and version=1
2016-09-28T08:47:52.094971Z 13 Query delete from UNIT_ACTION where PARENT_ID=132241
2016-09-28T08:47:52.134999Z 13 Query update AUTHORIZATION set UNIT_ID=null where UNIT_ID=132241
2016-09-28T08:47:52.158014Z 13 Query delete from UNIT_DOCUMENT where PARENT_ID=132241
2016-09-28T08:47:52.248074Z 13 Query delete from UNIT_PRODUCT where UNIT_ID=132241
2016-09-28T08:47:52.315641Z 13 Query delete from UNIT_PROJECT where UNIT_ID=132241
2016-09-28T08:47:52.586008Z 13 Query delete from ITEM_ALTERNATIVE where ITEM_ID=132241
2016-09-28T08:47:52.853350Z 13 Query delete from AUTHORIZATION where ID=714491
2016-09-28T08:47:52.910835Z 13 Query delete from UNIT_REGISTRATION where ID=173505
2016-09-28T08:47:52.980887Z 13 Query delete from UNIT where ID=132241 and version=1
2016-09-28T08:47:53.133290Z 13 Query rollback
如您所见,ITEM_RELATION
中没有要删除的行,我期待的是:
0000-00-00T00:00:00.000000Z 13 Query delete from ITEM_RELATION where PARENT_ID=132241
0000-00-00T00:00:00.000000Z 13 Query delete from ITEM_RELATION where CHILD_ID=132241
显然事务被回滚,因为:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`edea2`.`item_relation`, CONSTRAINT `FK_ITEM_RELATION_CHILD_ID` FOREIGN KEY (`CHILD_ID`) REFERENCES `unit` (`ID`))
另一个奇怪的事情是 Hibernate 执行了一个(不必要的?)UPDATE
作为第一条语句。
然而,
这种不同的行为是否与 lowerItemRelations
/higherItemRelations
引用相同的实体类型(尽管在不同的 fields/columns 和不同的行上)这一事实有关?
这是一个错误还是有这样的行为的原因?
我尝试了什么:
- 初始化集合
- 初始化并清除集合(以触发 orphanRemoval)
em.remove()
em.remove(item)
之前的每个集合元素
没有成功。
到目前为止我发现的唯一可行方法是发出:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaDelete<ItemRelation> delete = builder.createCriteriaDelete(ItemRelation.class);
Root<ItemRelation> rel = delete.from(ItemRelation.class);
delete.where(builder.or(
builder.equal(rel.get(ItemRelation_.parent), managedItem),
builder.equal(rel.get(ItemRelation_.child), managedItem)));
em.flush();
em.remove(managedItem);
我在 Wildfly 10.1 上使用 Hibernate 5.2。2.Final。0.Final
谢谢
这里要求的是调用em.remove()
的地方:
@Stateless
@Local
public class PersistenceService implements Serializable
{
@PersistenceContext
private EntityManager em;
...
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public <T> void delete(T entity)
{
T managed;
if(!em.contains(entity))
{
Class<T> entityClass = EntityUtils.getClass(entity);
Object entityId = EntityUtils.getIdentifier(entity);
managed = em.find(entityClass, entityId);
}
else
{
managed = entity;
}
em.remove(managed);
// em.flush(); // just for debugging
}
}
我敢打赌 Unit
正在引用其他内容,并且不是您的 root 独有的 - ItemRelation
在这种情况下,因此无法删除。
您的映射表明 Unit
可以有多个 ItemRelations
,所以这很可能是这里的问题。您必须首先通过删除双方的相互引用来 "break" 关系(或者至少在持有 FK 的实体上,或者如果使用 join table 则其中的任何一个)
好的,这是一个错误。
我发现此行为与两个集合的延迟初始化有关,因此我提交了问题 HHH-11144 and produced a simple test case (also available on GitHub)。
简而言之,这发生在
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Item item = em.createQuery("select x from Item x where x.code = 'first'", Item.class).getSingleResult();
Set<ItemRelation> lowerItemRelations = item.getLowerItemRelations();
Hibernate.initialize(lowerItemRelations);
// initializing 'higherItemRelations' prevents orphanRemoval to work on 'lowerItemRelations'
Set<ItemRelation> higherItemRelations = item.getHigherItemRelations();
Hibernate.initialize(higherItemRelations);
lowerItemRelations.clear();
tx.commit();
em.close();
我有这些实体:
@Entity
public class Item extends Unit
{
// @Id is in superclass
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<ItemRelation> lowerItemRelations = new LinkedHashSet<>();
@OneToMany(mappedBy = "child", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<ItemRelation> higherItemRelations = new LinkedHashSet<>();
// this too is in superclass
@OneToMany(mappedBy = "unit", cascade = CascadeType.REMOVE, orphanRemoval = true)
@OrderBy("date")
protected Set<UnitRegistration> registrations = new LinkedHashSet<>();
...
}
@Entity
@Table(name = "ITEM_RELATION",
indexes = @Index(columnList = "PARENT_ID, CHILD_ID", unique = true))
public class ItemRelation extends AbstractEntity
{
// @Id is in superclass
@ManyToOne(optional = false)
@JoinColumn(name = "PARENT_ID")
private Item parent;
@ManyToOne(optional = false)
@JoinColumn(name = "CHILD_ID")
private Item child;
@NotNull
@Min(0)
@Column(nullable = false, columnDefinition = "INT DEFAULT 1 NOT NULL")
private int quantity = 1;
...
}
现在我只想执行一个简单的em.remove(item)
,但是Hibernate不会为lowerItemRelations
/higherItemRelations
.
DELETE
语句
相反,对于用 @OneToMany(mappedBy = "...", cascade = CascadeType.ALL/REMOVE, orphanRemoval=true)
注释的所有其他字段,它会发出语句。
这里有一小段 MySQL 日志片段:
2016-09-28T08:47:52.090453Z 13 Query update UNIT set CODE='CE13000003167', ... where ID=132241 and version=1
2016-09-28T08:47:52.094971Z 13 Query delete from UNIT_ACTION where PARENT_ID=132241
2016-09-28T08:47:52.134999Z 13 Query update AUTHORIZATION set UNIT_ID=null where UNIT_ID=132241
2016-09-28T08:47:52.158014Z 13 Query delete from UNIT_DOCUMENT where PARENT_ID=132241
2016-09-28T08:47:52.248074Z 13 Query delete from UNIT_PRODUCT where UNIT_ID=132241
2016-09-28T08:47:52.315641Z 13 Query delete from UNIT_PROJECT where UNIT_ID=132241
2016-09-28T08:47:52.586008Z 13 Query delete from ITEM_ALTERNATIVE where ITEM_ID=132241
2016-09-28T08:47:52.853350Z 13 Query delete from AUTHORIZATION where ID=714491
2016-09-28T08:47:52.910835Z 13 Query delete from UNIT_REGISTRATION where ID=173505
2016-09-28T08:47:52.980887Z 13 Query delete from UNIT where ID=132241 and version=1
2016-09-28T08:47:53.133290Z 13 Query rollback
如您所见,ITEM_RELATION
中没有要删除的行,我期待的是:
0000-00-00T00:00:00.000000Z 13 Query delete from ITEM_RELATION where PARENT_ID=132241
0000-00-00T00:00:00.000000Z 13 Query delete from ITEM_RELATION where CHILD_ID=132241
显然事务被回滚,因为:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`edea2`.`item_relation`, CONSTRAINT `FK_ITEM_RELATION_CHILD_ID` FOREIGN KEY (`CHILD_ID`) REFERENCES `unit` (`ID`))
另一个奇怪的事情是 Hibernate 执行了一个(不必要的?)UPDATE
作为第一条语句。
然而,
这种不同的行为是否与
lowerItemRelations
/higherItemRelations
引用相同的实体类型(尽管在不同的 fields/columns 和不同的行上)这一事实有关?这是一个错误还是有这样的行为的原因?
我尝试了什么:
- 初始化集合
- 初始化并清除集合(以触发 orphanRemoval)
em.remove()
em.remove(item)
之前的每个集合元素
没有成功。
到目前为止我发现的唯一可行方法是发出:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaDelete<ItemRelation> delete = builder.createCriteriaDelete(ItemRelation.class);
Root<ItemRelation> rel = delete.from(ItemRelation.class);
delete.where(builder.or(
builder.equal(rel.get(ItemRelation_.parent), managedItem),
builder.equal(rel.get(ItemRelation_.child), managedItem)));
em.flush();
em.remove(managedItem);
我在 Wildfly 10.1 上使用 Hibernate 5.2。2.Final。0.Final
谢谢
这里要求的是调用em.remove()
的地方:
@Stateless
@Local
public class PersistenceService implements Serializable
{
@PersistenceContext
private EntityManager em;
...
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public <T> void delete(T entity)
{
T managed;
if(!em.contains(entity))
{
Class<T> entityClass = EntityUtils.getClass(entity);
Object entityId = EntityUtils.getIdentifier(entity);
managed = em.find(entityClass, entityId);
}
else
{
managed = entity;
}
em.remove(managed);
// em.flush(); // just for debugging
}
}
我敢打赌 Unit
正在引用其他内容,并且不是您的 root 独有的 - ItemRelation
在这种情况下,因此无法删除。
您的映射表明 Unit
可以有多个 ItemRelations
,所以这很可能是这里的问题。您必须首先通过删除双方的相互引用来 "break" 关系(或者至少在持有 FK 的实体上,或者如果使用 join table 则其中的任何一个)
好的,这是一个错误。
我发现此行为与两个集合的延迟初始化有关,因此我提交了问题 HHH-11144 and produced a simple test case (also available on GitHub)。
简而言之,这发生在
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Item item = em.createQuery("select x from Item x where x.code = 'first'", Item.class).getSingleResult();
Set<ItemRelation> lowerItemRelations = item.getLowerItemRelations();
Hibernate.initialize(lowerItemRelations);
// initializing 'higherItemRelations' prevents orphanRemoval to work on 'lowerItemRelations'
Set<ItemRelation> higherItemRelations = item.getHigherItemRelations();
Hibernate.initialize(higherItemRelations);
lowerItemRelations.clear();
tx.commit();
em.close();