Spring data JPA

Clean database after each test case run

Adopted from How to clear database in Spring Boot tests?

DatabaseCleaner.java

package com.genie.api.junit.rule.support;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class DatabaseCleaner {

    private static Logger LOG = LoggerFactory.getLogger(DatabaseCleaner.class);

    @Autowired
    private DataSource dataSource;

    private List<String> tablesForClearing = null;

    private Set<String> tablesToExclude = new HashSet<String>();

    public void excludeTables(String... tableNames) {
        Collections.addAll(tablesToExclude, tableNames);
    }

    public void reset() {
        if (notPrepared()) {
            prepare();
        }
        executeReset();
    }

    private boolean notPrepared() {
        return tablesForClearing == null;
    }

    private void prepare() {
        try (var conn = dataSource.getConnection()) {
            var metaData = conn.getMetaData();
            var tableRefs = metaData.getTables(conn.getCatalog(), null, null, new String[] { "TABLE" });
            List<String> tablesForClearingTemp = new ArrayList<>();
            while (tableRefs.next()) {
                var tableName = tableRefs.getString("TABLE_NAME");
                if (tablesToExclude.contains(tableName)) {
                    continue;
                }
                tablesForClearingTemp.add(tableName);
            }
            tablesForClearing = tablesForClearingTemp;
            LOG.info("Prepared clean db command: {}", tablesForClearing);
        } catch (SQLException e) {
            LOG.error("Failed to prepare table names.", e);
        }
    }

    private void executeReset() {
        try (var conn = dataSource.getConnection()) {
            var reset = buildClearStatement(conn);
            reset.executeBatch();
        } catch (SQLException e) {
            LOG.error("Failed to remove rows.", e);
        }
    }

    private Statement buildClearStatement(Connection conn) throws SQLException {
        var reset = conn.createStatement();
        reset.addBatch("SET FOREIGN_KEY_CHECKS = 0");
        tablesForClearing.forEach(table -> {
            try {
                reset.addBatch(String.format("DELETE FROM %s", table));
            } catch (SQLException e) {
                LOG.error("Failed to add delete command.", e);
            }
        });
        reset.addBatch("SET FOREIGN_KEY_CHECKS = 1");
        return reset;
    }

}

DatabaseCleanerRule.java

Practices

  1. Use @Table(name="{project_name}_{table_name}") to get rid of potential name clash with reserved words.

References

Last updated

Was this helpful?