Несколько операций по выполнению DAO - PullRequest
1 голос
/ 06 августа 2010

Я пытаюсь выполнить несколько запросов в одном DAO-методе.Тест не пройден (данные не обновляются).Журналы без исключений.</p> <pre><code>public List<Domain> getNewDomains(final int maxAllowedItems, final Date timestamp) { return getJpaTemplate().execute(new JpaCallback<List<Domain>>() { @SuppressWarnings("unchecked") public List<Domain> doInJpa(EntityManager entityManager) throws PersistenceException { Calendar dayBefore = Calendar.getInstance(); dayBefore.setTime(timestamp); dayBefore.add(Calendar.HOUR, -24); List ids = entityManager.createQuery("SELECT d.id FROM domain d WHERE d.crawlDate IS NULL and (d.lastRead IS NULL OR d.lastRead <= :dayBefore ) ") .setParameter("dayBefore", dayBefore.getTime()) .setMaxResults(maxAllowedItems) .getResultList(); LOG.debug("new domain IDS : " + ids.toString()); if(ids.isEmpty()) { return new ArrayList<Domain>(); } int result = entityManager.createQuery("UPDATE domain d SET d.lastRead = :timestamp WHERE d.id IN (:ids)") .setParameter("timestamp", timestamp) .setParameter("ids", ids).executeUpdate(); LOG.debug("update result : " + result); return entityManager.createQuery("SELECT d FROM domain d WHERE d.id IN (:ids) ") .setParameter("ids", ids) .setMaxResults(maxAllowedItems) .getResultList(); } }); }


Первый выбор обработан правильно.Но при обновлении состояние поля «lastRead» остается прежним.

Тест: </p> <pre><code>@Test public void testGetNewItems() { List<Domain> items = domainDAO.getNewDomains(2, new Date()); Assert.assertNotNull(items); Assert.assertTrue(items.isEmpty()); String domainName = "example.com"; Domain domain1 = new Domain(domainName); domainDAO.save(domain1); String domainName2 = "example2.com"; Domain domain2 = new Domain(domainName2); domainDAO.save(domain2); Domain domain2FromDB = domainDAO.getByName(domainName2); Assert.assertEquals(domain2, domain2FromDB); Assert.assertNull(domain2FromDB.getCrawlDate()); domain2FromDB.setCrawlDate(new Date()); domainDAO.update(domain2FromDB); String domainName3 = "example3.com"; Domain domain3 = new Domain(domainName3); domainDAO.save(domain3); items = domainDAO.getNewDomains(2, new Date()); Assert.assertNotNull(items); Assert.assertEquals(2, items.size()); Assert.assertTrue(items.contains(domain1)); Assert.assertTrue(items.contains(domain3)); Assert.assertFalse(items.contains(domain2FromDB)); for (Domain item : items) { Assert.assertNotNull(item.getLastRead()); // FAILED assert } }

Должен ли я мигать после обновления?Как правильно обрабатывать несколько запросов?

1 Ответ

1 голос
/ 09 августа 2010

Обновление и удаление запросов считаются массовыми обновлениями в JPA и имеют разные правила. Массовые обновления выполняются непосредственно в базе данных, контекст постоянства (EntityManager) не будет обновляться с этими изменениями. Поэтому, когда вы запрашиваете контекст постоянства, он находит совпадающую сущность - по незнанию возвращает устаревшие данные.

В спецификации JPA это выглядит так:

Следует соблюдать осторожность при выполнении операций массового обновления или удаления, поскольку они могут привести к несоответствия между базой данных и сущностями в контексте активного постоянства. В общем, навалом Операции обновления и удаления должны выполняться только внутри транзакции в новом постоянном текст или до выборки или доступа к объектам, состояние которых может быть затронуто такими операциями.

У вас есть несколько вариантов решения проблемы.

Перепишите первый запрос, чтобы вернуть сущность, а не только идентификатор. и изменить сущность. Примерно так должно работать

public List<Domain> doInJpa(EntityManager entityManager) throws PersistenceException {
    Calendar dayBefore = Calendar.getInstance();
    dayBefore.setTime(timestamp);
    dayBefore.add(Calendar.HOUR, -24);

    List<Domain> domains = entityManager.createQuery("SELECT d FROM domain d WHERE d.crawlDate IS NULL and (d.lastRead IS NULL OR d.lastRead <= :dayBefore ) ")
        .setParameter("dayBefore", dayBefore.getTime())
        .setMaxResults(maxAllowedItems)
        .getResultList();

    if(domains.isEmpty()) {
        return new ArrayList<Domain>();
    }

    for(Domain d : domains) { 
        d.setLastRead(timestamp);
    }

    return domains;     
}

Теперь вам нужно выполнить один запрос, сущности будут синхронизированы с контекстом постоянства, и ваш тест должен пройти.

Если вы не можете обойти проблему с массовыми обновлениями, вызвав entityManager.refresh () для каждого домена - метод обновления обновит сущность с самым последним состоянием из базы данных.

...