I have created Spring integration test in my application. The problem is, one test doesn't rollback properly, leaves some stuff in database and causes subsequent tests to fail.
I have noticed, that tests works good if persisted entity is simple entity. The test fails, when entity is part of inheritance hierarchy and inheritance type is of type InheritanceType.JOINED. When I change it to InheritanceType.SINGLE_TABLE it desn't fail.
Below is the code: Test class:
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@ContextConfiguration(locations = {"classpath:beans/repository-beans.xml"})
public class OnlyParentRollbackProblemSpringTest {
@PersistenceContext
private EntityManager entityManager;
@Test
//PASSES
public void isDBEmptyTest_1() throws Exception {
Query countQuery = entityManager.createQuery("select count(qi) from " + ParentEntity.class.getSimpleName() + " qi");
int elementsCount = ((Long)countQuery.getSingleResult()).intValue();
assertThat(elementsCount).isEqualTo(0);
}
@Test
//PASSES
public void testThatInfluencesOtherTests() {
ParentEntity queueItem = new ParentEntity();
entityManager.persist(queueItem);
ParentEntity queueItem1 = new ParentEntity();
entityManager.persist(queueItem1);
// given
flush();
// when
Query deleteQuery = entityManager.createQuery("delete from " + ParentEntity.class.getSimpleName());
deleteQuery.executeUpdate();
flush();
// then
Query countQuery = entityManager.createQuery("select count(qi) from " + ParentEntity.class.getSimpleName() + " qi");
int elementsCount = ((Long)countQuery.getSingleResult()).intValue();
assertThat(elementsCount).isEqualTo(0);
}
@Test
//FAILS
public void isDBEmptyTest_2() throws Exception {
Query countQuery = entityManager.createQuery("select count(qi) from " + ParentEntity.class.getSimpleName() + " qi");
int elementsCount = ((Long)countQuery.getSingleResult()).intValue();
assertThat(elementsCount).isEqualTo(0);
}
private void flush() {
entityManager.flush();
entityManager.clear();
}
}
spring configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="no.mintra.offlinetrainingportal.infrastructure.persistence" />
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="H2DataSource" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
<property name="dataSource" ref="H2DataSource" />
<property name="jpaVendorAdapter" ref="H2JpaVendorAdaptor" />
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.hbm2ddl.auto" value="create-drop" />
</map>
</property>
</bean>
<bean id="H2DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver"/>
<property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MYSQL;LOCK_MODE=3"/>
</bean>
<bean id="H2JpaVendorAdaptor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
</bean>
Entities:
@javax.persistence.Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class ParentEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
public ParentEntity() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
@Entity
public class SecondChildEntity extends ParentEntity {
@Column(unique = true)
@NotNull
private Integer userId;
public SecondChildEntity(Integer userId) {
this.userId = userId;
}
public Integer getUserId() {
return userId;
}
}
And pom.xml file:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.8.1</junit.version>
<org.springframework.version>3.1.0.RELEASE</org.springframework.version>
<log4j.version>1.2.15</log4j.version>
<org.slf4j.version>1.5.8</org.slf4j.version>
<hibernate-core.version>3.6.8.Final</hibernate-core.version>
<hibernate-validator.version>4.1.0.Final</hibernate-validator.version>
<commons-dbcp.version>1.2.2</commons-dbcp.version>
<h2database.version>1.3.163</h2database.version>
<commons-collections.version>3.2.1</commons-collections.version>
<javax.servlet.version>2.5</javax.servlet.version>
<javax.validation.version>1.0.0.GA</javax.validation.version>
<fest.version>1.2</fest.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2database.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>${fest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<exclusions>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>ejb3-persistence</artifactId>
</exclusion>
</exclusions>
<version>${hibernate-core.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate-core.version}</version>
</dependency>
</dependencies>
This issue is specific to H2 DBMS.
H2Dialect
contains the following warning:
if ( !( majorVersion > 1 || minorVersion > 2 || buildId >= 139 ) ) {
log.warn(
"The {} version of H2 implements temporary table creation such that it commits " +
"current transaction; multi-table, bulk hql/jpaql will not work properly",
( majorVersion + "." + minorVersion + "." + buildId )
);
}
You face this issue because executing bulk delete
against entity with JOINED
inheritance involves creation of temporary table (see generated SQL), so that the first part of transaction gets committed.
As a workaround you can use native SQL queries instead of bulk HQL delete
query. Or just use another in-memory database such as HSQLDB.