关于DAO执行的几个操作

时间:2010-08-06 07:27:41

标签: hibernate spring jpa dao

我尝试在一个DAO方法中执行多个查询。测试失败(数据未更新)。日志没有例外。

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();
        }
    });
}

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”字段状态相同。

测试:

@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 个答案:

答案 0 :(得分:1)

更新&amp;删除查询在JPA中被视为批量更新,并具有不同的规则。批量更新直接在数据库上执行,持久性上下文(EntityManager)将不会使用这些更改进行更新。因此,当您查询持久性上下文时,它会找到匹配的实体 - 在不知不觉中返回过时的数据。

JPA规范如下:

  

执行批量更新或删除操作时应该小心,因为它们可能会导致   数据库与活动持久性上下文中的实体之间的不一致。一般来说,批量   更新和删除操作只应在新的持久性协议中的事务中执行   文本或在获取或访问其状态可能受此类操作影响的实体之前。

关于如何解决问题,您有几个选择。

重写第一个查询以返回实体而不仅仅是id。并修改实体。这样的事情应该有效

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()来解决批量更新问题,则刷新方法将使用数据库中的最新状态更新实体。