Pourquoi ma requête JQL renvoie-t-elle un résultat différent de la requête CriteriaBuilder équivalente?

Aug 17 2020

J'utilise Dropwizard Hibernate et j'ai des problèmes avec mes tests. J'ai simplifié cet exemple autant que possible. Je crée un Foo, je le mets à jour, puis j'essaye de le récupérer. L'utilisation d'une requête brute permet d'obtenir le résultat correct, mais la requête CriteriaBuilder équivalente n'intercepte pas la mise à jour. Qu'est-ce que je fais mal?

    @Test
    public void testFoo() {
        String id = "12345";

        // Create
        Foo foo = Foo.builder()
            .id(id)
            .name("old-name")
            .build();
        sessionFactory.getCurrentSession().replicate(foo, ReplicationMode.EXCEPTION);

        // Update
        database.inTransaction(() -> {
            CriteriaBuilder cb = sessionFactory.getCurrentSession().getCriteriaBuilder();
            CriteriaUpdate<Foo> update = cb.createCriteriaUpdate(Foo.class);
            Root<Foo> root = update.from(Foo.class);

            update.set(Foo_.name, "new-name");
            update.where(cb.equal(root.get(Foo_.id), id));
            int updated = sessionFactory.getCurrentSession().createQuery(update).executeUpdate();
        });

        // Select
        database.inTransaction(() -> {
            sessionFactory.getCurrentSession().flush(); // Not sure if this matters

            String newName = (String) sessionFactory.getCurrentSession()
                .createQuery("select name from Foo where id=:id")
                .setParameter("id", id)
                .getSingleResult();

            assertEquals("new-name", newName);
            log.error("New name is " + newName);

            CriteriaBuilder cb = sessionFactory.getCurrentSession().getCriteriaBuilder();
            CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
            Root<Foo> root = cq.from(Foo.class);
            cq.where(cb.equal(root.get(Foo_.id), id));
            Query query = sessionFactory.getCurrentSession().createQuery(cq);

            Foo foo2 = (Foo) query.getSingleResult();
            log.error("New name is " + foo2.getName()); // Prints "old-name"
        });
    }

Voici mon code de configuration:

@ExtendWith(DropwizardExtensionsSupport.class)
public class UpdateTest {
    private SessionFactory sessionFactory;

    public DAOTestExtension database = DAOTestExtension.newBuilder()
        .addEntityClass(Foo.class)
        .build();

    @BeforeEach
    public void setup() {
        sessionFactory = database.getSessionFactory();
    }
...
}

Je peux aussi montrer la Fooclasse, mais ce n'est pas très intéressant.

Réponses

1 ChristianBeikov Aug 18 2020 at 09:16

L' Fooinstance est toujours dans le cache L1, c'est-à-dire la session en cours qui n'est pas touchée par l'instruction de mise à jour. Étant donné qu'Hibernate / JPA doit conserver l'identité des objets entité d'une session, il ne peut pas effacer la session en raison de l'instruction de mise à jour. Habituellement, il faut effacer la session ou actualiser les instances après une instruction de mise à jour pour refléter à nouveau l'état actuel.

Essayez sessionFactory.getCurrentSession().clear();plutôt de le faire au début de votre transaction de lecture