如何在Hibernate中获取OneToMany关系修订

时间:2018-07-24 14:20:26

标签: hibernate-envers

我们有两个实体,例如Resource和NonFTECost。与我们之间的关系是OneToMany双向的。下面是实体

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table
public class Resource {

    @NotNull
    @Column(name = "NAME", nullable = false, length = 255)
    @Audited(withModifiedFlag = true)
    private String name;

    @OneToMany(mappedBy = "resource", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
    @Audited
    private List<NonFTECost> costings = new ArrayList<>();

    //other fields

}


@Getter
@Setter
@Table
@Entity
public class NonFTECost {

    @NotNull
    @Audited(withModifiedFlag = true, modifiedColumnName = "PAYMENT_MOD")
    private Payment payment;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "RESOURCE_CODE", nullable = false, foreignKey = @ForeignKey(name = FK_COST_DETAILS_RESOURCE_CODE))
    @Audited(withModifiedFlag = true, modifiedColumnName = "RESOURCE_CODE_MOD", targetAuditMode = RelationTargetAuditMode.AUDITED)
    private Resource resource;

    //other fields

}

现在,我将创建一个具有成本核算的资源,然后它将为每个审核表创建新的修订版。然后,我只更改了NonFTECost实体的付款字段,它将在NonFTECost_Aud表中创建新修订(这也是资源更新的一部分)。

问题:-在获取资源的修订版的同时,我想为该垂直资源实体获取NonFTECost的修订版。因为我想向用户显示诸如fieldName oldvalue newvalue

请帮助我解决问题。

1 个答案:

答案 0 :(得分:1)

通过在查询的特定修订实例上迭代该集合,您应该能够为NonFTECost的给定修订获取关联的Resource实体。

例如,假设我对Resource的修订5感兴趣

final Number revision = 5;

final AuditReader auditReader = auditReaderFactory.get( session );
final Resource resource = auditReader.find( Resource.class, resourceId, revision );
for ( Cost cost : resource.getCosts() ) {
  // do whatever with cost
}

现在您需要的是如何采用该Cost实例并找出发生了什么变化。由于您使用功能withModifiedFlags=true,因此我们可以特别使用forRevisionsOfEntityWithChanges

我要指出的一件事是,在您的映射场景中,如果您碰巧修改了成本实体,那么NonFTECost实体的修订版可能会比Resource的修订版高在没有专门对Resource进行修改的交易中。

请牢记这一点,您需要在for循环逻辑中加以说明。因此,在该循环中,我们需要基于Cost实例执行查询,并分别获取其修订历史记录。

// This list is an object array that contains the following 
//   Index 0 - The `Cost` entity again at the revision
//   Index 1 - The revision entity (contains revision number/timestamp)
//   Index 2 - The RevisionType: ADD, MOD, or DEL
//   Index 3 - Set<String> of property names that changed at this revision
List results = auditReader.createQuery()
    .forRevisionsOfEntityWithChanges( Cost.class, false )
    .add( AuditEntity.id().eq( cost.getId() ) )
    .addOrder( AuditEntity.revisionNumber().desc() )
    .setMaxResult( 2 )
    .getResultList(); 

如果结果列表仅包含1行,那么您知道几件事(假设没有数据修剪)

  1. 索引2应该是RevisionType.ADD
  2. 索引3包含Set<String>个在原始持久化期间设置的字段。
  3. 没有字段具有旧值,无论其插入持久值是什么。

如果结果列表包含2行,则需要在这里处理新旧值逻辑。您应该能够执行以下操作:

if ( results.size() == 2 ) {
  Object[] newArray = (Object[]) results.get( 0 );
  Object[] oldArray = (Object[]) results.get( 1 );
  Set<String> propertiesChanged = (Set<String>) newArray[3];
  for ( String propertyName : propertiesChanged ) {
    if ( "value".equals( propertyName ) ) {
      Double newValue = ( (NonFTECost) newArray[0] ).getValue();
      Double oldValue = ( (NonFTECost) oldArray[1] ).getValue();
    }
  }
}
else if ( results.size() == 1 ) {
  Object[] array = (Object[]) results.get( 0 );
  Set<String> propertiesChangedOnInsert = (Set<String>) array[3];
  // do whatever here
}

这不是最优雅的解决方案,但是可以。