Chris Williams
Chris Williams

Reputation: 12481

How can I test that a JPA save actually saves data?

I am using JPA with Spring and saving an entity in a test. In the process of writing a test to validate that an entity's relationship with another entity is correctly set up, I have come across a problem that I come across frequently. I have a test method (set to rollback) that:

  1. Creates entity
  2. Saves entity
  3. Flushes
  4. Retrieves entity
  5. Validates entity

The problem is that when I look at the Hibernate logs, I only see a single insert to the database where I'd expect to see an insert and then a select.

I know this is because Hibernate's trying to save me some time and knows that it's got the entity with the ID I'm trying to retrieve but that bypasses an important step: I want to make sure that the entity actually made it to the database and looks like what I thought it should. What's the best way to deal with this so I can test that the entity is actually in the database?

Note: I assume this involves somehow detaching the entity or telling Hibernate to clear its cache but I'm not sure how to do that when all I have access to is a JpaRepository object.

Some code:

public interface UserRepository extends JpaRepository<User, Long> {
    //...
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JpaConfig.class, // JpaConfig just loads our config stuff
    loader = AnnotationConfigContextLoader.class)
@TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {
    @Test
    @Transactional
    public void testRoles() {
        User user = new User("name", "[email protected]");

        // eventually more here to test entity-to-entity relationship

        User savedUser = userRepository.save(user);
        userRepository.flush();

        savedUser = userRepository.findOne(savedUser.getId());

        Assert.assertNotNull(savedUser);
        // more validation here
    }

}

Upvotes: 1

Views: 4754

Answers (2)

Alan Hay
Alan Hay

Reputation: 23246

You have two strategies:

  1. issue a native SQL query therefor bypassing any JPA cache.
  2. ensure the persistence context is cleared before reloading.

For (1) you can change your tests to extend the following Spring class which, in addition to automatically beginning/rolling back a transaction at the start/end of each test, will give you access to a Spring JdbcTemplate you can use to issue the native SQL.

http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.html

http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/jdbc/core/simple/SimpleJdbcTemplate.html

For (2) you can clear the persistence context by doing the following (where the EntityManagerFactory is injected into your test:

EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory).clear();

See the following base test class which I normally use and demonstrates the above and also allows for populating the database with known data before each test (via DBUnit).

https://github.com/alanhay/spring-data-jpa-bootstrap/blob/master/src/test/java/uk/co/certait/spring/data/repository/AbstractBaseDatabaseTest.java

(In fact in the above I am actually creating a new JdbcTemplate by injecting a datasource. Can't remember why...)

Upvotes: 1

Maarten Winkels
Maarten Winkels

Reputation: 2417

You basically want to test Hibernate's functionality instead of your own code. My first suggestion: don't do it! It is already tested and validated many times.

If you really want to test it, there are a couple of options:

  1. Execute a query (rather than a get. The query will get executed (you should see it in the log) and the result interpreted. The object you get back would still be the same object you saved, since that is in the session.
  2. You can evict the object from the session and then get it again. If you use SessionFactory.getCurrentSession(), you'll get the same season that the repository is using. With that you can evict the object.

Upvotes: 1

Related Questions