我有一个可由多个线程调用的事务方法。
为了避免在发生并发调用时StaleStateException
,我在休眠中使用了悲观锁定,但它没有按照我的预期工作,我仍然得到了StaleStateException
。然后我查看了日志,发现对象没有正确锁定,这是我的日志:
2014-09-17_19:12:19.078 INFO c.c.p.a.w.m.ImportExportManagerImpl - ************************Test Name: Requirement Coverage Test
2014-09-17_19:12:19.079 INFO c.c.p.a.w.m.ImportExportManagerImpl - ************************Test folder ID: 9312
2014-09-17_19:12:19.525 INFO c.c.p.a.w.m.ImportExportManagerImpl - Test Case Id: P072051933
2014-09-17_19:12:19.615 WARN org.hibernate.loader.Loader - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
Hibernate:
select
requiremen0_."RC_ITEM_ID" as RC_ITEM_ID1_1_,
requiremen0_."RC_ENTITY_ID" as RC_ENTITY_ID2_1_,
requiremen0_."RC_ENTITY_TYPE" as RC_ENTITY_TYPE3_1_,
requiremen0_.RC_REQ_ID as RC_REQ_ID4_1_
from
"OME6500_OM65_OME6500_R9_2_DB"."REQ_COVER" requiremen0_
where
requiremen0_."RC_ENTITY_ID"=?
Hibernate:
select
requiremen0_."RQ_REQ_ID" as RQ_REQ_ID1_2_0_,
requiremen0_."RQ_USER_03" as RQ_USER_2_2_0_
from
"OME6500_OM65_OME6500_R9_2_DB"."REQ" requiremen0_
where
requiremen0_."RQ_REQ_ID"=?
Hibernate:
select
"RC_ITEM_ID"
from
"OME6500_OM65_OME6500_R9_2_DB"."REQ_COVER"
where
"RC_ITEM_ID" =? for update
2014-09-17_19:12:19.631 INFO c.c.p.a.w.m.ImportExportManagerImpl - Requirement Coverages Size:1
2014-09-17_19:12:19.631 INFO c.c.p.a.w.m.ImportExportManagerImpl - Deleting requirement coverage id: 130967
2014-09-17_19:12:19.634 INFO c.c.p.a.w.m.ImportExportManagerImpl - Requirement in testcase table Size:1
2014-09-17_19:12:19.669 INFO c.c.p.a.w.m.ImportExportManagerImpl - Checking if requirement coverage exists: test case id: 51933, req id: 7760
Hibernate:
delete
from
"OME6500_OM65_OME6500_R9_2_DB"."REQ_COVER"
where
"RC_ITEM_ID"=?
我的交易方法在这里:
@Transactional(readOnly = false)
public void importTestCases(String domain, String project, List<TestCase> testCases,
Boolean onlyUpdateReqCover, Boolean foldersExist)
throws RequestFailureException, RESTAPIException, InvalidDataException,
UnAuthorizedOperationException {
setDBSchema(domain, project);
for (TestCase testCase : testCases) {
TestFolder testFolder = retrieveTestFolderFromPath(domain, project, testCase.getFolderPath(),
foldersExist, testCase);
Test test = new Test(testCase, testFolder);
ALMEntity almEntity = null;
LOGGER.info("************************Test Name: " + test.getName());
LOGGER.info("************************Test folder ID: " + test.getParent_id());
...
...
LOGGER.info("Test Case Id: " + existingTest.getQc_tcid());
List<RequirementCoverage> requirementCoverages = requirementCoverageDao.findAllFromTestId(Integer
.parseInt(existingTest.getId()));
LOGGER.info("Requirement Coverages Size:" + requirementCoverages.size());
for (RequirementCoverage requirementCoverage : requirementCoverages) {
LOGGER.info("Deleting requirement coverage id: " + requirementCoverage.getId());
requirementCoverageDao.delete(requirementCoverage);
}
...
...
}
}
这是我带锁定的DAO方法:
@Override
public List<RequirementCoverage> findAllFromTestId(int testId) {
List<RequirementCoverage> list = sessionFactory.getCurrentSession()
.createQuery("from RequirementCoverage where entityId = :testId")
.setLockOptions(new LockOptions(LockMode.PESSIMISTIC_WRITE)).setParameter("testId", testId)
.list();
return list;
}
从日志中可以看出,requirementCoverages
返回的findAllFromTestId
未锁定。只有在requirementCoverage
开始后,才会为每个for (RequirementCoverage requirementCoverage : requirementCoverages)
获取锁定。
所以我认为StaleStateException
的原因是一个线程获取列表requirementCoverages
并尝试访问每个对象,但同时requirementCoverages
已被另一个修改线程。
我是对的吗?有没有办法锁定列表requirementCoverages
阻止另一个线程访问它?任何帮助表示赞赏。
答案 0 :(得分:0)
我通过重构我的代码解决了这个问题。
我没有对几个实体的命名查询使用悲观锁定,而是对这些实体的父实体应用悲观锁定,并在读取时锁定它。
然后对这些子实体进行更新。
最后,释放父实体。
答案 1 :(得分:0)
使用LockMode.PESSIMISTIC_FORCE_INCREMENT还有另一个选项,请查看this solution。