Hibernate 验证从集合中移除的集合成员
Hibernate validates collection members removed from collections
我遇到了一个奇怪的现象。在简化形式中,我的实体 类 看起来像这样:
@Entity
@Audited
public class Product {
@Id
@Column(length = 32)
private String id = IdGenerator.createId();
/** Descriptive texts, samples, comments etc.; ONIX composite Product/OtherText */
@OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true)
@JoinColumn(name="product_id")
@Index(name="idx_prod_text")
private Set<Text> texts;
...
}
@Entity
@Audited
public class Text {
@Id
@Column(length = 32)
private String id = IdGenerator.createId();
@NotNull
@Column(nullable=false, length=3)
private String type;
...
}
HashCode 和 equals 在 id 字段上工作。
我已经构建了一个通用的预验证例程,用于以用户友好的方式自动更正和生成警告。具有空类型的文本的自动更正是从产品的集合中删除此文本(通过调用 product.getTexts().removeAll(entitiesToRemove)
)。
现在我得到了一个产品(之前从数据库中读取,因此附加到 Hibernate 会话),它附加了一个类型为 null 的文本。它是自动更正的,并且有问题的文本如上所述从集合中删除。
到目前为止,一切都很好;在调试器中检查产品表明文本不再位于文本集合中。但是当我调用 productRepository.saveAndFlush(product)
() 时,我得到一个
javax.validation.ConstraintViolationException: Validation failed for classes [de.vlx.metadatastore.model.product.Text] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='darf nicht null sein', propertyPath=type, rootBeanClass=class de.vlx.metadatastore.model.product.Text, messageTemplate='{javax.validation.constraints.NotNull.message}'}
]
这么看来,这段文字似乎仍由 Hibernate 验证?即使它从未在数据库中(当然也有 NotNull 约束),并且不再在 Product 的文本集合中?
更奇怪的是:当我试图通过在被移除实体的类型上设置一个虚拟值来修复这个问题时,我没有得到上述异常,但是 org.springframework.dao.DataIntegrityViolationException
抱怨类型是空。
是哪种魔法在起作用,我该怎么做才能解决这个问题?
使用过的版本:
休眠 4.1.7.Final
Spring-data-jpa 1.6.0.RELEASE
Spring-核心 3.2.9.RELEASE
更新:
通过我最近发现的密集调试,(因为产品以多种方式交付,如 xml 和 excel 文件,并且与当前版本的合并是一个相当复杂的过程)在这种情况下,所有文本来自删除了以前的版本并添加了更新,然后在中央验证例程中删除了错误的文本。但是 Hibernate 已经将添加存储在它的操作队列中,并希望在删除之前执行插入。并且插入与初始(空)值一起存储,因此更新有问题的文本没有帮助。
我仍然完全不知道该怎么办。看来我必须确保任何时候都没有无效的对象可以进入任何集合,因为事后修复将无济于事。
看起来你有两个选择:
- 确保文本未设置为空并保留 "unmodified" 并简单地删除条目。
- 或者在自动更正功能期间设置伪文本,实际上避免了
NotNull
约束。
当然第一种可能性会更好,因为我不确定设置伪文本是否会在删除语句之前触发更新语句。
更新:
我看到了有关托管实体问题的一些可能性(不需要这个,所以未经测试):
- 使用
entityManager.refresh
刷新清理代码中的 Text
个实体。这将发出选择,但应该避免该问题。
- 也许你可以玩当前会话中的
evict
?不知道这里hibernate会有什么反应。
- 最好避免使用中间 DTO 而不是实体来修改
Text
值,并将它们合并到您的服务方法中。我认为这是我的首选解决方案。
经过大量的调试和测试,我终于找到了问题的原因和解决方法。
Hibernate 确实保留了一个 ActionQueue,其中保留了对托管实体的所有相关操作,并且大部分按照它们在实体上完成的顺序进行。就我而言,例如两个 Text 对象被添加到 Product 的文本集合中,因此 Hibernate 在其 ActionQueue 中添加了一个 InsertAction,其中包含插入时的值(因此后来对实体的修复没有效果,但向 ActionQueue 添加了一个 UpdateAction)。从集合中删除错误对象导致此对象的 DeleteAction。
这些 UpdateAction 或 DeleteAction 永远不会执行,因为 InsertAction 因 NotNull-Constraint 而失败。
所以我必须确保插入的对象永远不会有任何非法的空值。为此,我重构了验证服务,以便 before 插入具有可识别的虚拟值 != null 的实体,我稍后会用它来检查。不太好,但它有效。
我仍然对 Hibernate ActionQueue 的一种优化器感兴趣;自 4.1.7 版以来是否实现了类似的功能?
特别感谢 MartinFrey 提出的宝贵建议。
我遇到了一个奇怪的现象。在简化形式中,我的实体 类 看起来像这样:
@Entity
@Audited
public class Product {
@Id
@Column(length = 32)
private String id = IdGenerator.createId();
/** Descriptive texts, samples, comments etc.; ONIX composite Product/OtherText */
@OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true)
@JoinColumn(name="product_id")
@Index(name="idx_prod_text")
private Set<Text> texts;
...
}
@Entity
@Audited
public class Text {
@Id
@Column(length = 32)
private String id = IdGenerator.createId();
@NotNull
@Column(nullable=false, length=3)
private String type;
...
}
HashCode 和 equals 在 id 字段上工作。
我已经构建了一个通用的预验证例程,用于以用户友好的方式自动更正和生成警告。具有空类型的文本的自动更正是从产品的集合中删除此文本(通过调用 product.getTexts().removeAll(entitiesToRemove)
)。
现在我得到了一个产品(之前从数据库中读取,因此附加到 Hibernate 会话),它附加了一个类型为 null 的文本。它是自动更正的,并且有问题的文本如上所述从集合中删除。
到目前为止,一切都很好;在调试器中检查产品表明文本不再位于文本集合中。但是当我调用 productRepository.saveAndFlush(product)
() 时,我得到一个
javax.validation.ConstraintViolationException: Validation failed for classes [de.vlx.metadatastore.model.product.Text] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='darf nicht null sein', propertyPath=type, rootBeanClass=class de.vlx.metadatastore.model.product.Text, messageTemplate='{javax.validation.constraints.NotNull.message}'}
]
这么看来,这段文字似乎仍由 Hibernate 验证?即使它从未在数据库中(当然也有 NotNull 约束),并且不再在 Product 的文本集合中?
更奇怪的是:当我试图通过在被移除实体的类型上设置一个虚拟值来修复这个问题时,我没有得到上述异常,但是 org.springframework.dao.DataIntegrityViolationException
抱怨类型是空。
是哪种魔法在起作用,我该怎么做才能解决这个问题?
使用过的版本: 休眠 4.1.7.Final Spring-data-jpa 1.6.0.RELEASE Spring-核心 3.2.9.RELEASE
更新: 通过我最近发现的密集调试,(因为产品以多种方式交付,如 xml 和 excel 文件,并且与当前版本的合并是一个相当复杂的过程)在这种情况下,所有文本来自删除了以前的版本并添加了更新,然后在中央验证例程中删除了错误的文本。但是 Hibernate 已经将添加存储在它的操作队列中,并希望在删除之前执行插入。并且插入与初始(空)值一起存储,因此更新有问题的文本没有帮助。
我仍然完全不知道该怎么办。看来我必须确保任何时候都没有无效的对象可以进入任何集合,因为事后修复将无济于事。
看起来你有两个选择:
- 确保文本未设置为空并保留 "unmodified" 并简单地删除条目。
- 或者在自动更正功能期间设置伪文本,实际上避免了
NotNull
约束。
当然第一种可能性会更好,因为我不确定设置伪文本是否会在删除语句之前触发更新语句。
更新: 我看到了有关托管实体问题的一些可能性(不需要这个,所以未经测试):
- 使用
entityManager.refresh
刷新清理代码中的Text
个实体。这将发出选择,但应该避免该问题。 - 也许你可以玩当前会话中的
evict
?不知道这里hibernate会有什么反应。 - 最好避免使用中间 DTO 而不是实体来修改
Text
值,并将它们合并到您的服务方法中。我认为这是我的首选解决方案。
经过大量的调试和测试,我终于找到了问题的原因和解决方法。
Hibernate 确实保留了一个 ActionQueue,其中保留了对托管实体的所有相关操作,并且大部分按照它们在实体上完成的顺序进行。就我而言,例如两个 Text 对象被添加到 Product 的文本集合中,因此 Hibernate 在其 ActionQueue 中添加了一个 InsertAction,其中包含插入时的值(因此后来对实体的修复没有效果,但向 ActionQueue 添加了一个 UpdateAction)。从集合中删除错误对象导致此对象的 DeleteAction。
这些 UpdateAction 或 DeleteAction 永远不会执行,因为 InsertAction 因 NotNull-Constraint 而失败。 所以我必须确保插入的对象永远不会有任何非法的空值。为此,我重构了验证服务,以便 before 插入具有可识别的虚拟值 != null 的实体,我稍后会用它来检查。不太好,但它有效。
我仍然对 Hibernate ActionQueue 的一种优化器感兴趣;自 4.1.7 版以来是否实现了类似的功能?
特别感谢 MartinFrey 提出的宝贵建议。