2

我有一个可以由多个线程调用的事务方法。

为了避免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阻止另一个线程访问它?任何帮助表示赞赏。

4

2 回答 2

0

还有另一个使用 LockMode.PESSIMISTIC_FORCE_INCREMENT 的选项,请查看此解决方案

于 2015-04-30T14:32:57.163 回答
0

我通过重组代码解决了这个问题。

我没有对一些实体的命名查询使用悲观锁定,而是对这些实体的父实体应用悲观锁定并在读取时锁定它。

然后对这些子实体进行更新。

最后,释放父实体。

于 2014-09-22T16:33:46.630 回答