Java 是否从Hibernate persistenceContext缓存中排除JPA实体?
我正在用Spring和Hibernate4.3.6支持的JPA构建一个web应用程序以实现持久性。一些背景:作业结果存储在存储库中,结果控制器会对其进行检查。前端使用长轮询,因此ResultsController创建一个DeferredResult对象,然后派生一个线程来定期检查作业的完成情况,以便填写延迟结果并触发响应Java 是否从Hibernate persistenceContext缓存中排除JPA实体?,java,spring,hibernate,jpa,Java,Spring,Hibernate,Jpa,我正在用Spring和Hibernate4.3.6支持的JPA构建一个web应用程序以实现持久性。一些背景:作业结果存储在存储库中,结果控制器会对其进行检查。前端使用长轮询,因此ResultsController创建一个DeferredResult对象,然后派生一个线程来定期检查作业的完成情况,以便填写延迟结果并触发响应 private DeferredResultsResponse getResults(String idString, String runType, boolean retu
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)));
}
}
private DeferredResultsResponse getResults(字符串idString、字符串runType、布尔返回onNotDone){
字符串userId=userService.getCurrentUser();
//一些基本检查;ID是否为有效格式等不相关,
//但是,如果我们发现问题,“response”变量就会被设置
final DeferredResultsResponse deferredResult=新的DeferredResultsResponse(runId、runType、userId、returnOnNotDone);
if(响应!=null){
deferredResult.setResult(响应);
}否则{
线程t=新线程(()->completeResult(deferredResult));
t、 run();
}
返回延迟结果;
}
私有void completeResult(最终延迟结果响应结果){
final ResultsIdentifier id=新的ResultsIdentifier(result.getJobId(),result.getRunType());
int attemptCount=0;
布尔returnononfinished=result.isreturnonfinished();
而(!result.hassresult()&&attemptCount
问题在于:问题行上的查询永远不会报告作业状态的更改。经过一番观察,我找到了冬眠的内脏。在SessionImpl中,有一个StatefulPersistenceContext类型的字段,它保存着JobStatus对象从第一次从数据库中取出时的副本。然后在同一会话中的所有后续查询中重用该副本
现在,我想我可以通过获取当前会话并调用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);
公共接口JobStatusService{
/**
*检索当前用户的所有作业的状态。
*@返回所有作业的状态
*/
@事务(只读=真)
Iterable getAllByUser();
/**
*检索特定作业的状态
*@param identifier作业ID和类型的组合
*@返回持久化作业状态
*/
@事务性(readOnly=true,传播=propagation.REQUIRES\u NEW)
可选get(结果标识符);
/**
*保存已通过状态,如果未设置,则在当前用户的ID中进行子绑定,
*以及更新“上次更新”的时间
*@param status作业状态对象
*@返回持久化状态对象
*/
@事务(只读=假)
@修改
JobStatus保存(JobStatus状态);
/**
*删除特定作业的状态
*@param identifier作业ID和类型的组合
*/
@事务(只读=假)
@修改
作废删除(ResultsIdentifier标识符);
/**
*删除给定用户id的所有存储的作业状态。
*@param userId用户id
*/
@事务(只读=假)
@修改
void clearByUser(字符串userId);
ResultController创建一个DeferredResult对象,然后派生一个线程
实际上,不,它不是这样做的。你从不启动线程。你在同一个线程中执行所有操作,并且只在completeResult()
返回延迟结果后才返回。要真正启动线程,你必须替换
t.run();
借
现在,为了确保问题行始终转到数据库并重新加载作业状态的新值,您应该做的是确保jobStatusService.get()
在单独的事务中运行。使用Spring,通常通过使用
@Transactional(propagation = Propagation.REQUIRES_NEW)
FWIW这不是“hbm”文件。它是一个“orm.xml”,是JPA的一部分。真的!对不起,项目的发起人最初将它们全部命名为*.hbm.xml,尽管它就在xmlns中,但我没有费心更改名称。我尝试了一下,它似乎不起作用,传播也不受支持,听起来也很有希望。(任何使会话无效/清除/更改会话的操作!)在OP中添加了JobStatusService,以防其他批注交互不好导致您的建议失败。此外,在更正线程错误(感谢您发现!)后,我发现
completeResult()
开始抛出关于根本没有会话的异常。因此
@Transactional(propagation = Propagation.REQUIRES_NEW)