JPA 实体中的预/后加载更新

Posted

技术标签:

【中文标题】JPA 实体中的预/后加载更新【英文标题】:Pre/post load update in the JPA entity 【发布时间】:2014-12-04 11:50:27 【问题描述】:

我在 Spring Data JPA 和 Hibernate 中有项目。

有两个 JPA 实体 - 用户和作业。每个用户有多个工作。每个作业都有字段状态。创建新作业时,会向远程服务器发送一个新请求,并以 IN PROGRESS 状态保存作业。

更新后的状态可以通过远程服务器的ID查询。请求的业务逻辑应该是总是返回存储的状态,但是当Job处于IN PROGRESS状态时,应该向远程服务器查询更新状态,之后应该存储。

应该解释我意图的伪代码:

@Entity
class User 
    private long id;
    private String username;
    private Collection<Jobs> jobs;


@Entity
class Job 
    private long id;
    private User user;
    private Status status;
    @Inject
    private JobRepository jobRepository;

    @PrePersist
    private setDefaultStatus() 
        status = IN_PROGESS;
    

    @PostLoad
    private checkUpdatedStatus() 
        if (this.status == IN_PROGESS) 
            this.status = askRemoteServerForJobStatus(this.id);
            jobRepository.save(this);
        
    

伪代码用法:

userRepsitory.find(1).jobs // should trigger checkUpdatedStatus
jobsRepository.findAll() // should trigger checkUpdatedStatus

我认为在 Job 实体中包含 jobRepository 依赖项不是一个好习惯(如伪代码所示),因此我当前的实现在存储库中完成了所有这些 - 我有像 findAll() 这样的方法调用类似 PostLoad/checkUpdatedStatus 的方法。但我也从所有其他存储库中调用它,例如用户存储库,它也获取作业项。

在我看来,这是一个非常合理的用例,所以我不相信,没有更好的方法可以做到这一点。有没有人做过类似的事情?如果你能让我走上正轨,我会非常高兴。

谢谢你, vlastikcz

【问题讨论】:

看起来不像是正确的方法。如果远程服务器无法通过状态更新进行回调,我会考虑编写一个计划任务,该任务会定期轮询远程服务器以查询当前标记为“进行中”的任何作业的状态并相应地更新。 【参考方案1】:

将存储库注入实体不是一个好的举措。实体是持久结构,存储库应该管理实体,而不是相反。考虑事务分界和瞬态/分离实例。

有一种更好的方法可以在添加实体时调用某个登录名。您可以使用 event listener for this。您可以配置一个负载侦听器,它也是一个 Spring bean,因此您可以在此侦听器中注入其他服务/存储库。

【讨论】:

【参考方案2】:

最后,@Alan 的评论让我重新思考,我想出了不同的方法。

创建 Job 后,我启动一个异步任务,它只是轮询远程服务器直到它收到结果。

像这样:

@HandleAfterCreate
public void addNewRemoteJob(Job job) 
    remoteJobDispatcher.sendRequest(job);
    remoteJobDispatcher.waitForTheResult(job);


class RemoteJobDispatcher 
    @Async
    public void waitForTheResult(Job job) 
        while(true) 
            status = askRemoteServerForJobStatus(job)
            if (status != IN_PROGRESS) 
                job.setStatus(status);
                jobRepository.save(job);
                return;
            
            sleep(2000);
        
    

可能有类似的计划或启动作业来处理可能的意外应用程序状态,但没关系。

谢谢!

【讨论】:

以上是关于JPA 实体中的预/后加载更新的主要内容,如果未能解决你的问题,请参考以下文章

如何使用条件(where子句)更新实体并在spring数据jpa中的方法响应中获取更新的实体

更新应用程序后加载错误的 xib

即使在更新 SHA 1 指纹以获取发布密钥后,Google 地图也无法在 Play 商店中发布后加载

JPA:仅更新特定字段

JPA:仅更新特定字段

Spring JPA:使用更新的子实体更新实体