2014-09-17 1 views
2

У меня есть транзакционный метод, который может быть вызван несколькими потоками.Hibernate Pessimistic Locking не работает для базы данных Oracle?

Чтобы избежать 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

Существует еще один вариант использования LockMode.PESSIMISTIC_FORCE_INCREMENT, выписка this solution.