Spring junit test using Entity Manager does not insert data into H2 filesystem database

First my setup:

  • JUnit version: 4.11
  • Version H2: 1.3.174
  • Spring version: 4.0.5.RELEASE
  • Hibernate version: 4.1.0.Final

A little context:

I have a REST web service that is deployed to a Tomcat web server and has an h2 database underneath it. I have a REST service that has no POST / PUT methods. While writing the integration test, I manually added the entries to the DB using the H2 console and put the h2 file on the server. Finally, my integration test calls the REST service and the data that I manually entered into the DB is returned and the test succeeds. This is not supported and it would be great to bring in the data I need for each test (this approach can be used for other integration tests later ...). The goal is to inject data into the same database as the application deployed to Tomcat.

I thought it would be very easy, and I wrote an integration test that reuses the same application context used on the server side:

 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="org.h2.Driver" />
  <property name="url" value="${database.url}" />
  <property name="username" value="" />
  <property name="password" value="" />
</bean>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="be.wiv_isp.healthdata.catalogue.domain" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
        </props>
    </property>
</bean>

<bean id="jpaVendorAdaptor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emf" />
</bean>

      

where the database url is defined in the fil properties:

database.url=jdbc:h2:file:${healthdata.working.dir}/database/database-catalogue;AUTO_SERVER=true

      

Then I annotated the EntityManager as my PersistenceContext and wrote a simple unit test:

@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:/applicationContext-it-test.xml"})
public class H2Test {

    @PersistenceContext
    private javax.persistence.EntityManager em;

    DataCollectionDefinition dcd = new DataCollectionDefinitionBuilder()
        .withId(Long.valueOf(1))
        .build();

    @Before
    public void init() {
        em.persist(dcd);
        em.flush();
        em.clear();
    }

    @Test
    public void testGet() {
        DataCollectionDefinition found = em.find(DataCollectionDefinition.class, 1);
        Assert.assertEquals(found, dcd);
    }
}

      

This test runs fine! However, when I put a breakpoint after the data was flushed using the EntityManager and I connect to my H2 filesystem database, nothing is injected!

Now I was wondering. Is it normal that JUnit using Spring NEVER actually stores data in the database and stores it somewhere in memory? And there is a way to save it anyway, so I could use it to prepopulate the database for my integration tests.

At the moment I have a temporary solution to store my data using old old JDBC, but it is messy and I think it should work using Spring and above all I would like to understand why the data is not being persisted using the EntityManager Spring ...

+3


source to share


2 answers


What happens is a combination of how JPA works and what Spring does when executing transactional tests.

Firstly, JPA, when an object is saved in JPA, it does not mean that the data is actually saved in the database. Typically, data will be transferred to the database when the current transaction is running or when the Object Manager is reset. Check it out . Answer many of them, which you can find with a simple Google search.



Second, when Spring runs a test that is annotated with @Transactional

, then by default it rolls back from the transaction when the test completes. Look this one at many similar problems. To override this default behavior, you need to use @Rollback(false)

.

The data is written to a file when using JDBC, because you are executing statements outside of the Spring transaction generated for the test, and therefore rollback has no data to return by default.

+6


source


Add @Commit at the class or method level. Check this



When annotated at the class level, @Commit defines the default commit semantics for all test methods in the test class hierarchy. When declared with method-level annotation, @Commit defines the commit semantics for a particular test method, potentially overriding the default commit or rollback semantics at the class level.

+1


source







All Articles