0

I'm trying to tweak the delete behaviour of the domain model of my application. Let's say I have Countries and Regions, and Countries should not be deleted if they have any Regions. The other classes / configuration will follow below, but the following code should test that. countryRepository.flush(); throws a DataIntegrityViolationException, as expected, but the final call to countryRepository.count() throws the same Exception. In the logs you can see it's trying to execute the DELETE SQL command again.

I was hoping on the @Transactional to rollback the situation, but apparently it doesn't - perhaps because it's caught by assertThrows?

@SpringBootTest
@SpringBootApplication
@TestPropertySource(locations = "classpath:unit-tests.properties")
@Transactional
public class Tests {
    @Autowired
    private CountryRepository countryRepository;

    @Autowired
    private RegionRepository regionRepository;

    @Test
    public void preventDeleteOfUsedObjects() {
        // Create objects
        Country country = new Country();
        country.name = "United Status";
        countryRepository.save(country);
        countryRepository.flush();
        Region region = new Region();
        region.name = "California";
        region.country = country;
        regionRepository.save(region);
        regionRepository.flush();

        // Deleting used countries should not be possible
        Assertions.assertThrows(DataIntegrityViolationException.class, () -> {
            countryRepository.delete(country);
            countryRepository.flush();
        });

        Assert.assertEquals(1, countryRepository.count());
    }
}

The data model:

@Entity
@Table(name = "countries")
public class Country {
    @Id
    public String name;
}
@Entity
@Table(name = "regions")
public class Region {
    @Id
    public String name;

    @ManyToOne
    public Country country;
}

the corresponding repositories:

public interface CountryRepository extends JpaRepository<Country, String> {
}
public interface RegionRepository extends JpaRepository<Region, String> {
}

unit-tests.properties:

spring.datasource.url=jdbc:hsqldb:mem:model;sql.syntax_pgs=true
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.hsqldb.jdbcDriver
spring.jackson.default-property-inclusion=non_null
spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
logging.level.root=INFO

pom.xml:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
        <relativePath/>
    </parent>
    <artifactId>example</artifactId>
    <name>example</name>
    <properties>
        <maven.compiler.release>11</maven.compiler.release>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
Glorfindel
  • 21,988
  • 13
  • 81
  • 109

1 Answers1

0

After some more experimenting, I discovered that Spring will automatically rollback all changes after completing a test method, which I totally did not expect, given the SQL INSERT and DELETE statements I saw in the application log. I should've known they were executed after a BEGIN TRANSACTION, but could not easily check the results because I'm using HSQLDB for the unit tests without a management tool. And I have other 'unit tests' in the project (not marked @Transactional, and a different database connection in the properties file) to generate some bootstrap data in a PostgreSQL database, which work as expected and do persist data.

So for now I'll just end the test without

Assert.assertEquals(1, countryRepository.count());

hoping I won't be stupid enough to catch DataIntegrityViolationExceptions in my main code...

Glorfindel
  • 21,988
  • 13
  • 81
  • 109