I am attempting to improve the speed of our flyway database tests. The original method was to run flyway.clear()
and flyway.migrate()
between each test run. The problem though is now we have some potentially long running flyway migration scripts and we don't want to have to re-run those between each test.
I have been trying to use the spring @Transactional annotation to roll back the database after each test, then we only need to run flyway once.
The problem is, as soon as I add transactions around the test JPA stops fetching the foreign keys. I am assuming this is because the nested transactions aren't behaving as I expect, but I haven't been able to figure out what I have configured wrong.
I have posted an example project here: ExampleSpringJpaHibernatePostgresqltest with the problem (See ExampleTest.java
) but in summary:
I have the entities with a foreign key as follows (i have removed unnessisary to make this easier to read):
@Entity
@Table(name = "parent")
public class ParentEntity {
@Id
@Column
private Integer id;
@Column
private String name;
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "fk_parent", referencedColumnName = "id")
private Set<ChildEntity> children;
}
@Entity
@Table(name = "child")
public class ChildEntity {
@Id
@Column
private Integer id;
@Column
private String name;
@Column(name = "fk_parent")
private Integer fkParent;
}
I then have a test that uses parentRepository.findAll()
. If I surround that test using @Transactional
then the test fails because parentEntity.getChildren()
is null. Without the transaction though, and running flyway clear/migrate between each test everything works as expected.
@Test
@Transactional
public void testReadWithDependencies() {
ParentEntity savedParent1 = parentRepo.save(new ParentEntity("parent1"));
childRepo.save(new ChildEntity("child1", savedParent1.getId()));
Set<ChildEntity> acutalChildren = parentRepo.findAll().get(0).getChildren();
// XXX When the tests are run with @Transactional, this assertion fails.
assertThat(acutalChildren).isNotNull();
}
parentRepo.findAll
does not refresh the data from the database and you don't add the child to the children in parent.
Therefore there are no children assigned to the parent entity.
So if you refresh the entity (I injected the EntityManger) your test is successful:
@Test
@Transactional
public void testReadWithDependencies() {
assertThat(parentRepo.findAll()).isEmpty();
assertThat(childRepo.findAll()).isEmpty();
ParentEntity savedParent1 = parentRepo.save(new ParentEntity("parent1"));
childRepo.save(new ChildEntity("child1", savedParent1.getId()));
List<ParentEntity> allParents = parentRepo.findAll();
assertThat(allParents).hasSize(1);
// Refresh
ParentEntity parentEntity = allParents.get(0);
entityManager.refresh(parentEntity);
Set<ChildEntity> acutalChildren = parentEntity.getChildren();
// XXX When the tests are run with @Transactional, this assertion fails.
assertThat(acutalChildren).isNotNull();
assertThat(acutalChildren).hasSize(1);
assertThat(acutalChildren.iterator().next().getName()).isEqualTo("child1");
}