Hibernate验证从集合中删除的集合成员

时间:2015-04-08 12:14:55

标签: java spring hibernate spring-data-jpa

我遇到了一个奇怪的现象。在简化形式中,我的实体类看起来像这样:

@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字段上工作。 我已经建立了一个通用的预验证例程,用于以用户友好的方式自动校正和生成警告。对具有null类型的文本进行自动更正是从产品中的集合中删除此文本(通过调用product.getTexts().removeAll(entitiesToRemove))。

现在我得到了一个产品(以前从DB中读取,因此附加到Hibernate会话),它会附加一个类型为null的Text。它是自动更正的,如上所述,有问题的文本将从集合中删除。 到现在为止还挺好;在调试器中检查产品表明Text不再出现在文本集合中。但是当我打电话给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约束),并且不再出现在产品的文本集合中了?

更奇怪的是:当我尝试通过在被删除实体的类型上设置虚拟值来修补此问题时,我没有得到上述异常,但是org.springframework.dao.DataIntegrityViolationException抱怨类型为null的事实。

哪种魔法起作用,我该怎么做才能解决这个问题?

二手版本: Hibernate 4.1.7.Final Spring-data-jpa 1.6.0.RELEASE Spring-core 3.2.9.RELEASE

更新: 通过密集的调试,我最近发现,(因为产品以多种方式提供,如xml和excel文件,并且与当前版本的合并是一个相当复杂的过程),在这种情况下,以前版本的所有文本都被删除并且更新添加,然后在中央验证例程中删除错误文本。但是Hibernate已将添加存储在其动作队列中,并希望在删除之前执行插入。并且插入与初始(null)值一起存储,因此更新有问题的文本没有帮助。

我仍然完全无能为力。似乎我必须确保任何时候都没有无效对象可以输入任何集合,因为之后的修复无济于事。

2 个答案:

答案 0 :(得分:1)

看起来你有两个选择:

  • 确保文本未设置为null并保持&#34;未修改&#34;并简单地删除条目。
  • 或者在自动更正功能期间设置伪文本,实际上避免了NotNull约束。

当然第一种可能性更可取,因为我不确定设置伪文本是否会在删除语句之前触发更新语句。

更新: 我看到了托管实体问题的一些可能性(不需要这个,所以未经测试):

  • 使用Text刷新清理代码中的entityManager.refresh个实体。这将发出选择,但应该避免这个问题。
  • 可能你可以在当前会话中使用evict吗?不知道冬眠会如何反应。
  • 理想情况下避免使用中间DTO而不是实体来修改Text值,并将它们合并到服务方法中。我认为这是我的首选解决方案。

答案 1 :(得分:0)

经过大量的调试和测试后,我终于找到了问题的原因和解决方法。

Hibernate确实保留了一个ActionQueue,其中保留了对托管实体的所有相关操作,并且主要按照它们在实体上完成的顺序。在我的情况下有例如两个Text对象添加到Product中的文本集合中,因此Hibernate在其ActionQueue中添加了一个InsertAction,其中包含插入时的值(因此后来修复实体确实没有效果,但是将UpdateAction添加到ActionQueue)。从集合中删除有缺陷的对象导致该对象的DeleteAction。

从不执行这些UpdateAction或DeleteAction,因为InsertAction失败并带有NotNull-Constraint。 所以我必须确保插入的对象永远不会有任何非法的空值。为此,我重构了验证服务,以便在之前插入具有可识别虚拟值的实体!= null,我稍后会用它来检查。不太好,但它确实有效。

我仍然对Hibernate ActionQueue的一种优化器感兴趣;从版本4.1.7开始实现了类似的东西吗?

特别感谢MartinFrey的一些有价值的建议。