【问题标题】:Spring Data JPA @EntityListeners w/ @Transient @PostLoad, Properties Not UpdatedSpring Data JPA @EntityListeners w/ @Transient @PostLoad,属性未更新
【发布时间】:2018-01-25 23:48:35
【问题描述】:

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

家长:

@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 和 transactions/sessions 和 EntityListeners 有什么误解?

PS。 AutowireHelper 引用自here

【问题讨论】:

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


    【解决方案1】:

    我相信您误解了三个不同的响应式回调 @PostPersist、@PostUpdate 和 @PostLoad 之间的细微差别。

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

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

    在处理 Spring Data 时,当您在 Repository 上调用 save 方法时,该方法可能导致持久性提供程序调用 persist 或 merge 操作,具体取决于实体对象是否是瞬态的/new 或者它是否是现有的、可能分离的实体实例。

    也就是说,您可能需要一系列侦听器回调来管理您所追求的生命周期。这是因为当您修改 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&lt;ParentView&gt; 传递给上层而不是List&lt;Parent&gt;。

    【讨论】:

    • 对不起,如果我误解了这一点,所以基本上这只是更新父类中的一个字段来触发父类中的实体监听器?
    • 不完全是。 OP 想要一种在子对象的状态发生更改时通知父对象的方法,这会影响父对象的某些状态。前者旨在充当从子实体生命周期到父实体的回调。后者旨在说明瞬态可以在本质上包装实体的装饰器对象内单独管理。后者的好处是您可以避免不必要的 cpu 循环,这些回调来自 child->parent,而是在需要时将该过程转换为委托,特别是如果您只在 UI/biz 逻辑中需要它。
    • 我不确定我的情况是否相同。我的问题是当我只更新我的子表的字段时,我的父类的 entitylistener 没有接收到这个更改,或者它可能检测到我的父级没有更改,所以它没有被调用。为什么这很重要,因为我正在使用我父母的“lastModifiedDate”字段来查看记录是否已更新,但如果仅修改了子表,则此 lastModifiedDate 不会改变。现在我只是在每次更新完成时从我的控制器切换我的父类中的一个布尔字段。
    • 因此,当子项被修改时,让子项的实体侦听器调用父项上执行其实体侦听器逻辑的方法。这样,当无论如何修改父级或子级时,您可以保证两个实体侦听器场景都会调用您父级的业务逻辑。然后您应该可以删除布尔值。
    • 我明白了,这听起来合乎逻辑。但是,如果我的父母有很多子表,那是否意味着我必须为每个孩子创建(例如 10 个)不同的实体侦听器才能调用父母?如果是这种情况,那么我会将其视为取决于不同情况的情况。只是想弄清楚解决方案就是这样。但是感谢您回复旧帖子! :)
    猜你喜欢
    • 2014-04-16
    • 1970-01-01
    • 2015-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-28
    • 1970-01-01
    • 2017-06-30
    相关资源
    最近更新 更多