简单的问题:有没有人有任何想法如何保护某些实体不被CascadeType.ALL
在运行时中的hibernate
删除(可能会引发运行时异常)?
说,我们有一些实体:
@Entity
@Table(name = "FOO_ENTITY")
public class FooEntity {
...
}
我想保护它免受意外的错误映射,例如:
@Entity
@Table(name = "SOME_OTHER_FOO_ENTITY")
public class SomeOtherFooEntity {
...
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "FOO_ENTITY_ID")
private FooEntity fooEntity;
}
因此,应该可以通过FooEntity
删除某个session.delete(fooEntityObj)
类型的实体,但必须禁用它才能通过级联删除(session.delete(someOtherFooEntityObj)
)删除它。
注意:对于那些不专心地读我的问题或认为我不明白我在问什么的人:
1)我无法移除 CascadeType.ALL
。问题是:谁编程避免和保护这个?
2)单元测试不是方法,我正在寻找运行时解决方案。
答案 0 :(得分:2)
其中一种方法是以编程方式检查Hibernate映射元数据,并检查是否有任何删除操作(ALL
,REMOVE
,orphanRemoval
)级联到受保护的来自任何其他实体的实体;类似的东西:
String protectedEntityName = FooEntity.class.getName();
SessionFactoryImpl sessionFactory = (SessionFactoryImpl) session.getSessionFactory();
for (EntityPersister entityPersister : sessionFactory.getEntityPersisters().values()) {
for (int i = 0; i < entityPersister.getPropertyTypes().length; i++) {
Type type = entityPersister.getPropertyTypes()[i];
EntityType entityType = null;
if (type.isCollectionType()) {
CollectionType collectionType = (CollectionType) type;
Type elementType = sessionFactory.getCollectionPersister(collectionType.getRole()).getElementType();
if (elementType.isEntityType()) {
entityType = (EntityType) elementType;
}
} else if (type.isEntityType()) {
entityType = (EntityType) type;
}
if (entityType != null && entityType.getName().equals(protectedEntityName)) {
if (entityPersister.getPropertyCascadeStyles()[i].doCascade(CascadingAction.DELETE)) {
// Exception can be thrown from here.
System.out.println("Found! Class: " + entityPersister.getEntityName() + "; property: " + entityPersister.getPropertyNames()[i]);
}
}
}
}
此验证可以在服务器启动或集成测试中执行。
这种方法的优点是你不必修改Hibernate定义的行为;它只是提醒您忘记不要将删除级联到FooEntity
。
关于测试,是的,我知道OP明确表示测试不是这个用例的可接受解决方案(我个人同意它)。但是这些类型的自动测试可能是有用的,因为你写它们并忘记它们;您无需在添加新映射或修改现有映射时更新测试(由于您可能忘记或监督对每个可能的用例采用测试,因此无法完成测试的目的)。
答案 1 :(得分:1)
对于初学者,我认为你确实理解了你的要求,你刚刚确定了一个特定的解决方案,许多人,包括我自己,都在质疑。这不是疏忽......它试图解决你的实际问题。
如果您确实希望停止注释的CascadeType.ALL
值,使其具有记录的效果,而不是验证CascadeType.ALL
未使用的位置(而不是通过单元测试验证这些期望) ),然后扩展DefaultDeleteEventListener
并覆盖deleteEntity方法,始终将false传递给isCascadeDeleteEnabled
标志的超级实现。
如果您想要一个具有某种标准的预期行为的解决方案,那么定义应该在架构级别执行级联删除的关系,并建立最佳实践以仅使用您关注的CascadeType
你的代码。也许那是PERSIST和MERGE,也许你正在使用会话工厂的保存和更新功能,所以你需要使用特定于Hibernate的@CascadeType
注释。
答案 2 :(得分:0)
如果您不希望将更改级联到与真正更改的对象关联的对象,那么您是否只能从cascade
注释中删除@ManyToOne
属性?
答案 3 :(得分:0)
捕获任何编程错误的最可靠方法是编写单元测试。
如果你练习Test Driven Development,你将尽量减少忘记&#34;这样做。