java - 从 Hibernate persistenceContext 缓存中排除 JPA 实体?

标签 java spring hibernate jpa

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

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 对象时就保留了该对象的副本。然后,它在同一 session 中的所有后续查询中重用该副本。

现在,我认为可以通过获取当前 session 并调用clear()或refresh(status)来解决这个问题。然而,对我来说,当其他地方都通过 Spring/JPA 存储库进行中介时,必须拉开 JPA 的帷幕并直接使用 Hibernate 的东西是很糟糕的形式。那么,有什么方法可以标记 ORM XML 文件以排除特定类型在 PersistanceContext 中缓存吗?

<小时/>

作为引用,这里是 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 是一个只包含没有子元素的单个元素的元素。

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

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);

最佳答案

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

事实上,不,它不是这么做的。你永远不会启动线程。您在同一个线程中执行所有操作,并且只有在 completeResult() 返回后才返回延迟结果。要真正启动一个线程,您必须替换

t.run();

t.start();

现在,为了确保问题行始终进入数据库并重新加载作业状态的新值,您应该做的是确保 jobStatusService.get() 在单独交易。使用 Spring,通常通过使用注释方法来完成

@Transactional(propagation = Propagation.REQUIRES_NEW)

关于java - 从 Hibernate persistenceContext 缓存中排除 JPA 实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29505231/

相关文章:

java - XLSX文件读取错误

java - hibernate中删除相关对象

java - Spring data JPA中findBy和findOneBy的区别

spring - 如何使 Spring Security OAuth2 真正无状态/摆脱 "state"参数?

spring - ConversionNotSupportedException : Failed to convert property value of type 'grails.spring.BeanBuilder' to required type 'java.lang.String'

java - Spring上下文配置

java - 如何在文件中的特定位置写入内容

java - 为什么我会收到方法覆盖错误?

java - 无法使 Maven 测试范围的依赖项与 Java 9(或 10)模块一起使用

java - 当有新的未覆盖代码时如何使构建失败?