2010-08-06 13 views
1

J'essaie d'exécuter plusieurs requêtes dans une méthode DAO. Le test a échoué (les données ne sont pas mises à jour). Journaux sans exceptions. Plusieurs opérations à l'exécution 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(); 
     } 
    }); 
} 


sélectionnez d'abord traité correctement. Mais sur la mise à jour "lastRead" état du champ la même chose.

Test:

@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 
    } 
} 

doit clignoter I après la mise à jour? Quelle est la manière correcte de traiter plusieurs requêtes?

Répondre

1

Mise à jour & supprimer les requêtes sont considérées comme des mises à jour en vrac dans JPA, et ont des règles différentes. Les mises à jour en masse s'exécutent directement sur la base de données. Le contexte de persistance (EntityManager) ne sera pas mis à jour avec ces changements. Ainsi, lorsque vous interrogez le contexte de persistance, il trouve une entité correspondante - renvoyant sans le savoir des données périmées.

La spécification JPA il met comme ceci:

Une attention particulière doit être utilisé lors de l'exécution mise à jour en vrac ou de suppression, car ils peuvent entraîner des incohérences entre la base de données et les entités dans le contexte de persistance active. En général, en vrac mise à jour et de suppression ne doivent être effectuées dans une transaction dans une nouvelle persistance Con- texte ou avant de récupérer ou d'accéder à des entités dont l'état pourrait être affecté par ces opérations.

Vous avez quelques options pour résoudre le problème.

Réécrire la première requête pour renvoyer l'entité au lieu de simplement l'identifiant. et modifiez l'entité. Quelque chose comme cela devrait fonctionner

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

vous êtes à une seule requête, les entités seront synchronisées avec le contexte de persistance et de votre test devrait passer. Si vous ne parvenez pas à contourner le problème avec des mises à jour groupées en appelant entityManager.refresh() sur chaque domaine, la méthode refresh actualisera l'entité avec l'état le plus récent de la base de données.