views:

48

answers:

3

I have a JUnit 4 test case with the Spring @Transactional annotation that saves an object, and then attempts to find it. The test case passes when I use this implementation:

@Override
public EventSummary findEventSummaryById(Integer id) {
    return em.find(EventSummary.class, id);
}

It fails when I use this implementation (and then change which method I call in the test case):

@Override
public EventSummary findEventSummary(Integer id) {
    Query query = em.createQuery("select es from EventSummary as es where es.id = :id");
    query.setParameter("id", id);
    EventSummary result = (EventSummary) query.getSingleResult();
    return result;
}
A: 

in the first case, the id is an Integer

in the second, the id is a String

an Integer will never equal a String

LES2
Shoot, copy and paste error. My code does indeed pass in an Integer. Sorry.
pkananen
+3  A: 

Тhe entity is in the current session (entity manager) - it is in persistent state, waiting to be flushed. The get method first checks the contents of the session, and if not found turns to the underlying database. In your case, the entity has just been saved in the same session, so it is found and returned.

Update: it turned out the problem is using an incorrect transaction manager, hence the entity hasn't been flushed to the database. See Pascal's explanation.

Bozho
I was thinking along these lines as well. It turns out it's a bit more complicated. Actually, the test case now passes for each scenario without an explicit flush. However, `@Transactional` in Spring 2.5.6 assumes that your transaction manger is named `transactionManager`, but in this case my JPA transaction manager was called `txManager`, but I also had a JDBC transaction manager called `transactionManager`. So, the test case wasn't joining the same transaction as the DAO because it was using the JDBC manager instead of the JPA manager, and thus wasn't able to see the object in the database.
pkananen
I'm sorry but the first part of this answer is incorrect, at least not when using the default flush mode and when performing queries inside a transaction. In such case, JPA guarantees you won't get stale or incorrect results. So this answer doesn't explain the problem.
Pascal Thivent
+3  A: 

If you are using the default flush mode (AUTO) and if you are executing your query within a transaction, the JPA specification guarantees that a Query will not return stale or incorrect data:

3.6.2 Queries and FlushMode

The flush mode setting affects the result of a query as follows.

When queries are executed within a transaction, if FlushModeType.AUTO is set on the Query object, or if the flush mode setting for the persistence context is AUTO (the default) and a flush mode setting has not been specified for the Query object, the persistence provider is responsible for ensuring that all updates to the state of all entities in the persistence context which could potentially affect the result of the query are visible to the processing of the query. The persistence provider implementation may achieve this by flushing those entities to the database or by some other means. If FlushModeType.COMMIT is set, the effect of updates made to entities in the persistence context upon queries is unspecified.

public enum FlushModeType {
    COMMIT,
    AUTO
}

If there is no transaction active, the persistence provider must not flush to the database.

Assuming that you are using AUTO, check the transactional side.

Pascal Thivent
You are correct, and I made note of that in my comment on the other answer. This was fundamentally a transaction manager problem, that was masked as another problem.Moral of the story: `@Transactional` in Spring 2.5.6 assumes that your transaction manager is named `transactionManager`, but that is NOT the one I was using for my DAO. So you are right, the transactional side was incorrect.
pkananen