从Hibernate persistenceContext缓存中排除JPA实体? [英] Excluding a JPA entity from Hibernate persistenceContext caching?

查看:108
本文介绍了从Hibernate persistenceContext缓存中排除JPA实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Spring构建一个Web应用程序,并由Hibernate 4.3.6支持JPA以实现持久性.一些背景:作业结果存储在存储库中,并且ResultsController对其进行检查.前端使用长时间轮询,因此ResultsController创建一个DeferredResult对象,然后分离出一个线程以定期检查作业是否完成,以便它可以填充延迟的结果并触发响应.

I'm building a web-app with Spring, and JPA backed by Hibernate 4.3.6 for persistence. Some background: job results are being stored in a repository, and a ResultsController checks on them. The front-end uses long polling, so the ResultsController creates a DeferredResult object and then spins off a thread to check periodically for completion of the job so that it can fill in the deferred result and trigger the response.

private DeferredResultsResponse getResults(String idString, String runType, boolean returnOnNotDone) {
    String userId = userService.getCurrentUser();

    // Some basic checks; is the ID a valid format, etc. Not relevant,
    // but the "response" variable is set if we find a problem

    final DeferredResultsResponse deferredResult = new DeferredResultsResponse(runId, runType, userId, returnOnNotDone);
    if (response != null) {
        deferredResult.setResult(response);
    } else {
        Thread t = new Thread(() -> completeResult(deferredResult));
        t.run();
    }

    return deferredResult;
}

private void completeResult(final DeferredResultsResponse result) {
    final ResultsIdentifier id = new ResultsIdentifier(result.getJobId(), result.getRunType());
    int attemptCount = 0;
    boolean returnOnUnfinished = result.isReturnOnUnfinished();

    while (!result.hasResult() && attemptCount < MAX_ATTEMPTS) {
        attemptCount++;
 // ------- Problem line: -----------
        Optional<JobStatus> statusMaybe = jobStatusService.get(new ResultsIdentifier(result.getJobId(), result.getRunType()));

        if (!statusMaybe.isPresent()) {
            result.setResult(new ResultsResponse(null, false, null, "Unable to find job status entry."));
            continue;
        }

        JobStatus status = statusMaybe.get();
        // Incomplete job cases: sleep or respond "not done" based on the flag
        if (!status.isComplete() && returnOnUnfinished) {
            result.setResult(new ResultsResponse(false, false, null, null));
            continue;
        } else if (!status.isComplete()) {
            sleep();
            continue;
        }

        // Cases of completion: respond based on success
        // Various business logic of processing results
    }
    if (!result.hasResult()) {
        result.setResult(new ResultsResponse(true, false, null, String.format("Time out after %d checks", MAX_ATTEMPTS)));
    }
}

问题是这个:问题行上的查询永远不会报告作业状态的变化.经过一番寻找,我将其追溯到了Hibernate的内部.在SessionImpl中,有一个类型为StatefulPersistenceContext的字段,它从第一次将JobStatus对象从数据库中拉出时一直保留它的副本.然后,它将在同一会话中的所有后续查询上重用该副本.

The issue is this: the query on the Problem Line doesn't ever report changes in the job's status. After some looking, I tracked this down to the innards of Hibernate. In SessionImpl there is a field of type StatefulPersistenceContext, and it is keeping a copy of the JobStatus object from the first time it's pulled out of the database. It then reuses that copy on all subsequent queries in the same session.

现在,我收集到可以通过获取当前会话并调用clear()或refresh(status)来解决此问题的方法.但是,对我来说,当通过Spring/JPA存储库介导的其他任何地方时,不得不撤回JPA的门而直接使用Hibernate的东西是不好的形式.那么,有什么方法可以标记一个ORM XML文件,以将特定类型排除在PersistanceContext中之外?

Now, I gather that I can solve this by getting the current session and calling clear(), or refresh(status). However, to me, having to pull back the JPA curtain and use Hibernate stuff directly when everywhere else it's mediated through the Spring/JPA repositories is bad form. So, is there any way to mark up an ORM XML file to exclude a particular type from being cached in the PersistanceContext?

作为参考,这里是JobStatus.xml:

For reference, here is JobStatus.xml:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
             version="2.0">
<entity class="project.model.JobStatus">
<attributes>
  <embedded-id name="jobIdentifier" />

  <basic name="complete" optional="false" />
  <basic name="userId" optional="false" />

  <basic name="successful" optional="true" />
  <basic name="message" optional="true" />

  <basic name="lastUpdateTime" optional="false">
    <temporal>TIMESTAMP</temporal>
  </basic>
</attributes>
</entity>
</entity-mappings>

jobIdentifier是一个元素,它只包含一个没有子元素的元素.

jobIdentifier is an which contains nothing but the single element with no children.

此外,这是带有事务注释的JobStatusService:

Also, here is the JobStatusService with transaction annotations:

public interface JobStatusService {

/**
 * Retrieve the statuses of all jobs for the current user.
 * @return All jobs' statuses
 */
@Transactional(readOnly = true)
Iterable<JobStatus> getAllByUser();

/**
 * Retrieve the status of a particular job
 * @param identifier the combined job ID and type
 * @return  The persisted job status
 */
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
Optional<JobStatus> get(ResultsIdentifier identifier);

/**
 * Save the passed status, subbing in the current user's ID if none is set,
 * and updating the "last updated" time
 * @param status the job status object
 * @return  The persisted status object
 */
@Transactional(readOnly = false)
@Modifying
JobStatus save(JobStatus status);

/**
 * Delete the status of a particular job
 * @param identifier the combined job ID and type
 */
@Transactional(readOnly = false)
@Modifying
void remove(ResultsIdentifier identifier);

/**
 * Remove all stored job statuses for the given user id.
 * @param userId User id
 */
@Transactional(readOnly = false)
@Modifying
void clearByUser(String userId);

推荐答案

ResultsController创建一个DeferredResult对象,然后产生一个线程

the ResultsController creates a DeferredResult object and then spins off a thread

实际上,不,不是那样做的.您永远不会启动线程.您正在同一线程中执行所有操作,并且仅在返回completeResult()后才返回延迟的结果.要真正启动线程,您必须替换

Actually, no, that's not what it does. You never start the thread. You're executing everything in the same thread, and you only return the deferred result once completeResult() has returned. To really start a thread, you would have to replace

t.run();

作者

t.start();

现在,要确保问题行始终进入数据库并重新加载作业状态的新值,您应该做的是确保jobStatusService.get()在单独的事务中运行.使用Spring,通常是通过使用

对该方法进行注释来完成的

Now, to make sure the problem line always goes to the database and reloads the new value of the job status,what you should do is to make sure that jobStatusService.get() runs in a separate transaction. Using Spring, that is typically done by annotating the method with

@Transactional(propagation = Propagation.REQUIRES_NEW)

这篇关于从Hibernate persistenceContext缓存中排除JPA实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆