Spring Data JPA @EntityListeners w / @Transient @PostLoad,Properties Not Updated

时间:2018-01-25 23:48:35

标签: hibernate jpa spring-data spring-data-jpa entitylisteners

我们有两个Spring Data JPA实体(父级,子级),字段设置为特定值的子级计数,影响@Transient属性集中@PostLoad属性集中父级记录的值}

父:

@Entity
public class Parent {
   @Transient
   private boolean status = false;

   @OneToMany
   @Where("STATUS = true")
   private Set<Children> childrens;

   @PostLoad
   public void postload(){ 
     if(childrens.size() > 0) this.status = true;
   }
....
} 

儿童:

@Entity
@EntityListeners({ ParentListener.class })
public class children {

      private Boolean status = false;

      @ManyToOne
      private Parent parent;
}

在我的控制器/服务类中( NOT 注释为@Transactional),我来了并更新Children记录的状态值:

@Service 
public class ChildrenService {

...
    public void doStuff(Children child) {
        child.status = true;
        childRepository.save(child);
    }
} 

现在ParentListener开始了,我希望在父级状态值发生变化时进行记录。

class ParentListener {

    @PostUpdate // AFTER Children record updated
    public void childPostPersist(Children child) {
         AutowireHelper.autowire(this);
         // here child.parent.status == false (original value)
         // but I have set this child record equal to true, and 
         // have triggered the `save` method, but assuming I am still
         // in the session transaction or flush phase, the 
         // parent related record's status is not updated?
         System.out.println(child.parent.status); // prints false
         Parent currentParent = parentRepository.getOne(child.parent.getId());
         System.out.println(currentParent.status); // prints false
    }
}

我对@Transactional@Postload和交易/会话以及EntityListeners的误解是什么?

PS。 AutowireHelper是here

的引用

1 个答案:

答案 0 :(得分:2)

我相信你误解了三种不同的反应性回调@PostPersist@PostUpdate@PostLoad之间的微妙差异。

仅在实体首次加载到持久性上下文或正在刷新实体的状态时才会触发@PostLoad回调。前者在执行查找或查询时发生,后者在您在实体实例上调用refresh时发生。

类似地,@PostPersist回调在第一次持久化实体后触发,而@PostUpdate回调在现有实体更新后触发。

在处理Spring Data时,当您在save上调用Repository方法时,该方法可能会导致持久性提供程序调用{​​{1}}或persist操作取决于实体对象是临时的/新的还是现有的,可能是分离的实体实例。

也就是说,您可能需要一系列监听器回调来管理您所追求的生命周期。这是因为当您修改merge实体并保存它时,这不一定会在关联上传播侦听器回调。

Child

根据用例,如果您尝试为某些非持久性任务维护此瞬态状态,我可能建议在此使用装饰器模式而不是实体生命周期,因为保持不同的重要性首先是分开的。

实现此装饰器模式的一种方法可能包括以下内容:

public class Children {
   /**
    * When the change is persisted or updated, make sure to trigger
    * the callback on the parent to update its status accordingly
    */
   @PostPersist
   @PostUpdate
   public void updateParentAssociationStatusOnPersistOrUpdate() {
     if ( parent != null ) {
       parent.updateStatusOnPersistOrUpdateOrLoad();
     }
   }
}

public class Parent {
   /**
    * When the parent is loaded, refreshed, or after being persisted
    * or updated, this method will insure that the status based on 
    * child association is properly maintained.
    */
   @PostLoad
   @PostPersist
   @PostUpdate
   public void updateStatusOnPersistOrUpdateOrLoad() {
     if ( children != null && !children.isEmpty() ) {
       setStatus( true );
     }
   }
}

现在只需将// An interface that defines the parent's contract public interface Parent { default boolean getStatus() { return false; } // other methods } // Entity implementation of the parent contract @Entity(name = "Parent") public class ParentImpl implements Parent { // implement all non-default methods // #getStatus will be implemented in the decorator } // A view/decorator object that implements the parent contract public class ParentView implements Parent { private final Parent parent; public ParentView(Parent parent) { this.parent = parent; } // all methods delegate to the parent @Override public boolean getStatus() { return !parent.getChildren().isEmpty(); } } 传递给上层,而不是List<ParentView>