2017-08-11 64 views
3

我目前面临一个非常奇怪的问题。Bean验证@ElementCollection和@Version冲突并且验证失败

我有一个实体,其中包含属性是一个元素集合。

@ElementCollection(targetClass=Integer.class, fetch = FetchType.EAGER) 
@CollectionTable(name="campaign_publisher", [email protected](name="campaign_id")) 
@Column(name = "publisher_id") 

... 

@NotEmpty(message = "campaign.publishers.missing") 
public Set<Integer> getPublishers() { 
    return this.publishers; 
} 

public Campaign setPublishers(Set<Integer> publisherId) { 
    this.publishers = publisherId; 
    return this; 
} 

这一切工作正常。这些值经过验证并保存正确。

我也想,所以我申请一个@版本注解以及该实体拥有乐观并发。

@Version 
private Long etag = 0L; 

... 

public Long getEtag() { 
    return etag; 
} 

public void setEtag(Long etag) { 
    this.etag = etag; 
} 

通过添加@Version注释@NotEmpty验证我的发布者集合总是返回无效。

要尝试和诊断这个我试过如下:

  • 在实体层面创建自定义的验证,所以我可以在实体检查值。我发现Set的值已被替换为一个空的PersistentSet,导致验证始终失败。

  • 我创建了使用该从validationfactory检索到的验证,这似乎验证按预期方式工作的实体一些单元测试。

  • 我也试图给ElementCollection更改为许多一对多关系和双向一个一对多的,但问题仍然存在。

现在我不知道了。我唯一能找到的工作是在保存数据之前禁用hibernate验证并手动调用验证器。

所以我的问题是:

  • 有没有人遇到这个问题之前?
  • 关于接下来我可以尝试的任何建议?

谢谢大家的阅读!

回答

4

简短回答:设置初始值为etag = null

// this should do the trick 
@Version 
private Long etag = null; 

较长的一个:当您通过在字段中添加@Version批注与你正在休眠/弹簧数据的默认值增加一个乐观锁认为,实体是不是一个新的(甚至是ID为null)。因此,在初始保存而不是坚持实体undelying库尝试进行合并。并且暂时的实体力量将hibernate逐一复制,将源实体(即持久化的)的所有属性复制到目标实体(这是通过休眠将所有属性设置为默认值,即空值自动创建的),然后来到这个问题,因为hibernate只会复制FROM_PARENT类型的关联值,或者换句话说只有在实体端拥有的关联值,但在您的情况下关联值为TO_PARENT(从子级到父级的外键),hibernate将尝试推迟关联主实体保存后持久,但保存不起作用,因为实体不会通过@NotEmpty验证。

+0

嘿,谢谢你的回答。它帮助我解决了这个问题。解释也非常详细。当它让我时,我会奖赏你的赏金。再次感谢人! –

1

首先,我会建议删除您的@Version属性的默认值初始化。这个属性由hibernate维护,并且应该由它初始化。

第二:你确定你正在验证完全构造的实体吗?即你正在构建一些东西,然后做一些事情,并且为了确切的坚持/冲洗循环,你的实体处于错误状态。

为了澄清这一点,当你在一个春天的一面,我会建议你DAO层上引入服务水平的验证。即在初始调用DAO期间强制bean验证,而不是在flush期间对实体进行bean验证(yeap hibernate批量处理大量事情,并且仅在刷新周期中进行确切的验证)。

要做到这一点:标记您的DAO @Validated,使你的函数参数beign验证:FancyEntity store(@Valid @NotNull FancyEntity fancyEntity) { fancyEntity = em.persist(fancyEntity); em.flush(); return fancyEntity;}

通过使这一点,你一定会认为你是存储有效的实体:被称为存储方法之前会发生的验证。这将揭示你的实体变得无效的地方:在你的服务层,或在不良行为的休眠层。

0

我注意到你使用混合访问:方法和字段。在这种情况下,你可以尝试设置@Version的方法:

@Version 
public Long getEtag() { 
    return etag; 
} 

不是在球场上。

+0

感谢您的回答,我确实尝试过,但没有解决问题 –