默默忽略 remove()
Silently ignored remove()
存在实体 A 引用(多对一)实体 B,具有从 B 到 A 的反向(映射)引用。此外,还有对 C 的引用和对 A 的反向引用 C。
当我发出 entityManager.remove(A) 然后 flush() 时,"delete" 没有生成!但也没有例外。就像根本没有调用 remove() 一样。为什么会这样?
如果在 remove() 之前我们从反向引用 B.listOfA 和 C.listOfA 中提取 A,则按预期生成 "delete"。
另请注意 my another question 我得出的结论是 orphanRemoval 并不总是按预期工作。现在我开始怀疑级联可能工作得很好,但在那之后实际的级联删除是 "swallowed" 就像我在这里描述的那样。
看看这个。基本上,JPA 规范要求如果对删除的实体应用持久化操作,则该实体会再次被管理。
要验证这是否真的发生,请为 org.hibernate
包启用跟踪日志级别并搜索如下日志条目:
un-scheduling entity deletion ...
为避免任何不可预测的行为,建议从加载相同的所有其他实体实例中删除对已删除实体的引用 session/transaction。
我遇到了这个问题。
即使 show_sql 已启用:
<property name="hibernate.show_sql" value="true"></property>
没有输出,删除命令被静默忽略。
问题出在 OneToMany 关系上,其中:
待办事项 1 --- * 待办事项
环境是:
-JSF
- Wildfly 18(JPA 默认实现 - Hibernate)
- PostgreSQL 10
在任何一方尝试删除一个实例都会无提示地失败,服务器输出上没有相应的 "delete" 命令,也没有任何类型的异常消息。
解决方案
首先,我将 Wildfly18/JBoss 文档在其示例中建议的多方代码从:
// @Override
// public void delete(Todoitem todoitem) {
// if (todoitem == null) {
// return;
// }
// if (!emPg.contains(todoitem)) {
// todoitem = emPg.merge(todoitem);
// }
// emPg.remove(todoitem);
// emPg.flush();
// emPg.clear();
// }
等效形式:
@Override
public void delete(Todoitem todoitem) {
if (todoitem == null) {
return;
}
Todoitem ti = emPg.find(Todoitem.class, todoitem.getId());
if(ti == null) {
return;
}
emPg.remove(ti);
emPg.flush();
emPg.clear();
}
不幸的是,我得到了相同的结果 - 删除被默默地忽略了。
然后,我怀疑数据库,因为休眠处理它自己的密钥。我在实施过程中做了很多测试......
为了确定一致性问题,我决定清空数据库,删除关系双方的内容。
然后我使用应用程序的资源(没有 SQL 命令)创建了新的实体实例和关系,因为除了实体删除之外一切都运行良好。
之后,我使用 SQL 命令检查了数据库关系,以确保一切正常,确实如此。
接下来,我又尝试了一次新的测试,这次成功了。
然后我还原了代码,取消注释注释的代码,反之亦然,然后再次测试。
测试再次失败,返回相同的结果——默默地忽略删除。
我再次将代码设置回之前的条件测试以确认结果。
确认成功,确认了对数据库和代码实现两个问题的怀疑。
好消息,我在 "One" 端重复了同样的操作(下面的代码 - 注释代码是从 Wildfly 18 文档继承的初始代码,如上所述)。
发件人:
// @Override
// public void delete(Todo todo) {
// if(todo == null) {
// return;
// }
// if (!emPg.contains(todo)) {
// todo = emPg.merge(todo);
// }
// //todoitemDao.deleteAll(todo);
// emPg.remove(todo);
// emPg.flush();
// }
收件人:
@Override
public void delete(Todo todo) {
if (todo == null) {
return;
}
Todo t = emPg.find(Todo.class, todo.getId());
if(t == null) {
return;
}
emPg.remove(t);
emPg.flush();
emPg.clear();
}
再次重复测试,但这次不是删除 Todoitem 实例("many" 端)而是删除 Todo 实例("one" 端)。
问题已解决。成功。
重要提示:
直接在数据库上使用 SQL 语句检查删除测试。
有时由于缓存问题或代码实现错误,实例似乎没有被删除,需要分开处理。
存在实体 A 引用(多对一)实体 B,具有从 B 到 A 的反向(映射)引用。此外,还有对 C 的引用和对 A 的反向引用 C。 当我发出 entityManager.remove(A) 然后 flush() 时,"delete" 没有生成!但也没有例外。就像根本没有调用 remove() 一样。为什么会这样? 如果在 remove() 之前我们从反向引用 B.listOfA 和 C.listOfA 中提取 A,则按预期生成 "delete"。
另请注意 my another question 我得出的结论是 orphanRemoval 并不总是按预期工作。现在我开始怀疑级联可能工作得很好,但在那之后实际的级联删除是 "swallowed" 就像我在这里描述的那样。
看看这个
要验证这是否真的发生,请为 org.hibernate
包启用跟踪日志级别并搜索如下日志条目:
un-scheduling entity deletion ...
为避免任何不可预测的行为,建议从加载相同的所有其他实体实例中删除对已删除实体的引用 session/transaction。
我遇到了这个问题。 即使 show_sql 已启用:
<property name="hibernate.show_sql" value="true"></property>
没有输出,删除命令被静默忽略。
问题出在 OneToMany 关系上,其中: 待办事项 1 --- * 待办事项
环境是: -JSF - Wildfly 18(JPA 默认实现 - Hibernate) - PostgreSQL 10
在任何一方尝试删除一个实例都会无提示地失败,服务器输出上没有相应的 "delete" 命令,也没有任何类型的异常消息。
解决方案
首先,我将 Wildfly18/JBoss 文档在其示例中建议的多方代码从:
// @Override
// public void delete(Todoitem todoitem) {
// if (todoitem == null) {
// return;
// }
// if (!emPg.contains(todoitem)) {
// todoitem = emPg.merge(todoitem);
// }
// emPg.remove(todoitem);
// emPg.flush();
// emPg.clear();
// }
等效形式:
@Override
public void delete(Todoitem todoitem) {
if (todoitem == null) {
return;
}
Todoitem ti = emPg.find(Todoitem.class, todoitem.getId());
if(ti == null) {
return;
}
emPg.remove(ti);
emPg.flush();
emPg.clear();
}
不幸的是,我得到了相同的结果 - 删除被默默地忽略了。
然后,我怀疑数据库,因为休眠处理它自己的密钥。我在实施过程中做了很多测试...... 为了确定一致性问题,我决定清空数据库,删除关系双方的内容。 然后我使用应用程序的资源(没有 SQL 命令)创建了新的实体实例和关系,因为除了实体删除之外一切都运行良好。 之后,我使用 SQL 命令检查了数据库关系,以确保一切正常,确实如此。
接下来,我又尝试了一次新的测试,这次成功了。
然后我还原了代码,取消注释注释的代码,反之亦然,然后再次测试。 测试再次失败,返回相同的结果——默默地忽略删除。
我再次将代码设置回之前的条件测试以确认结果。 确认成功,确认了对数据库和代码实现两个问题的怀疑。
好消息,我在 "One" 端重复了同样的操作(下面的代码 - 注释代码是从 Wildfly 18 文档继承的初始代码,如上所述)。
发件人:
// @Override
// public void delete(Todo todo) {
// if(todo == null) {
// return;
// }
// if (!emPg.contains(todo)) {
// todo = emPg.merge(todo);
// }
// //todoitemDao.deleteAll(todo);
// emPg.remove(todo);
// emPg.flush();
// }
收件人:
@Override
public void delete(Todo todo) {
if (todo == null) {
return;
}
Todo t = emPg.find(Todo.class, todo.getId());
if(t == null) {
return;
}
emPg.remove(t);
emPg.flush();
emPg.clear();
}
再次重复测试,但这次不是删除 Todoitem 实例("many" 端)而是删除 Todo 实例("one" 端)。
问题已解决。成功。
重要提示:
直接在数据库上使用 SQL 语句检查删除测试。 有时由于缓存问题或代码实现错误,实例似乎没有被删除,需要分开处理。