1

Can anyone using DBRider / Test Container in spring boot give advice ?

I've been using DBRider/Testcontainer without any issue, but at some point , it doesn't work out.

It seesm HikariProxyPreparedStatement.execute() of DBUnit 's SimplePreparedStatement.addBatch() is being called, but could not insert any data.

Here's my test code that I've tried to verify initially.

( I've configured spring boot test code just like the example of DBRider, yet it seems like inserting the data kept failing. )


@Testcontainers
@SpringBootTest
@DBRider
@DBUnit(caseInsensitiveStrategy = Orthography.LOWERCASE)
class TestcontainersConfiguration {

    @Autowired
    private AdminRepository adminRepository;

    @Container
    static MySQLContainer<?> mySQLContainer = MySQLFixture.getInstance();

    static {
        JdbcDatabaseDelegate jdbcDatabaseDelegate = new JdbcDatabaseDelegate(mySQLContainer, "");
        ScriptUtils.runInitScript(jdbcDatabaseDelegate,"mock/sql/admin.sql");
    }

    @DynamicPropertySource
    static void overrideProps(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", mySQLContainer::getJdbcUrl);
        registry.add("spring.datasource.username", mySQLContainer::getUsername);
        registry.add("spring.datasource.password", mySQLContainer::getPassword);
    }

    @Test
    @DisplayName("")
    @DataSet(
        value = "mock/json/admin.json",
        strategy = SeedStrategy.INSERT,
        cleanBefore = true,
        disableConstraints = true,
        cleanAfter = true,
        transactional = true
    )
    @DisplayName("Succeded on extract / load admin")
    void succeedOnExtractAndLoad() {
        // GIVEN
        // WHEN
        List<Admin> all = adminRepository.findAll();

        // THEN
        assertFalse(all.isEmpty());
        all.stream().map(Admin::getAdmIdx).forEach(System.out::println);
    }

}

Afaik, DBRider utilizes @DBUnit , @TestExecutionListeners for Database Init / Call before test method.

I've made an assumption of

  1. Maybe connection is not configured to test container -> X (It was same connection)
  2. Maybe the version of test container matters -> X (I've tested all of the version, furthermore, there's no corelation between them if you looked at the code)
  3. Maybe prepared statement was not working at all for some reason -> X (I've mocked every logic of DBRider, all of them succeeded)
  4. Maybe invalid data format (.json) -> X ( Json was well formed, and could be able to parse it )

Here are my test codes to prove my assumptions. Be aware of singleton of test container instance.

@SpringBootTest
public class DBRiderInsertTest {

    @Autowired
    private AdminJpaRepository adminJpaRepository;

    private final static MySQLFixture MY_SQL_FIXTURE = MySQLFixture.getInstance();

    // Init db with schema
    static {
        JdbcDatabaseDelegate jdbcDatabaseDelegate = new JdbcDatabaseDelegate(MY_SQL_FIXTURE, "");
        ScriptUtils.runInitScript(jdbcDatabaseDelegate, "mock/sql/admin.sql");
    }

    private final DatabaseOperation databaseOperation = DatabaseOperation.INSERT;

    @Test
    @DisplayName("Succeded on extract / load admin")
    void succeedOnExtractAndLoad() throws SQLException, DatabaseUnitException, IOException {
        // GIVEN
        Connection dbConnection = DriverManager.getConnection(
            MY_SQL_FIXTURE.getJdbcUrl(),
            MY_SQL_FIXTURE.getUsername(),
            MY_SQL_FIXTURE.getPassword()
        );
        ConnectionHolderImpl connectionHolder = new ConnectionHolderImpl(dbConnection);
        DataSetExecutorImpl dataSetExecutor = DataSetExecutorImpl.instance(connectionHolder);
        IDataSet iDataSet = dataSetExecutor.loadDataSet("mock/json/admin.json");

        // WHEN
        RiderDataSource riderDataSource = dataSetExecutor.getRiderDataSource();
        DatabaseConnection dbUnitConnection = riderDataSource.getDBUnitConnection();
        databaseOperation.execute(dbUnitConnection, iDataSet);

        // THEN
        try (
            PreparedStatement stmt = dbConnection.prepareStatement("SELECT adm_idx FROM admin LIMIT 1");
            ResultSet rs = stmt.executeQuery()
        ) {
            boolean hasValue = rs.next();
            assertTrue(hasValue);
            int admIdx = rs.getInt("adm_idx");
            assertTrue(admIdx > 0);
            System.out.println("adm_idx = " + admIdx);
        } catch (SQLException e) {
            e.printStackTrace();
            fail("Query failed due to SQLException: " + e.getMessage());
        }
    }

    @Test
    @DisplayName("Succeded on verification of instance connection")
    void succededOnVerificationInstanceConnection() throws SQLException {
        // GIVEN
        // WHEN
        try (Connection connection = DriverManager.getConnection(
            MY_SQL_FIXTURE.getJdbcUrl(),
            MY_SQL_FIXTURE.getUsername(),
            MY_SQL_FIXTURE.getPassword())
        ) {

            // THEN
            assertTrue(connection.isValid(2), "Database connection is not valid");
        }
    }

    @Test
    @DisplayName("Succeded on parsing dataset of json.")
    void succededOnParsingDatasetJson() throws SQLException, DataSetException, IOException {
        // GIVEN
        Connection dbConnection = DriverManager.getConnection(
            MY_SQL_FIXTURE.getJdbcUrl(),
            MY_SQL_FIXTURE.getUsername(),
            MY_SQL_FIXTURE.getPassword()
        );
        ConnectionHolderImpl connectionHolder = new ConnectionHolderImpl(dbConnection);
        DataSetExecutorImpl dataSetExecutor = DataSetExecutorImpl.instance(connectionHolder);

        // WHEN
        IDataSet iDataSet = dataSetExecutor.loadDataSet("mock/json/admin.json");

        // THEN
        ITableIterator iterator = iDataSet.iterator();
        while (iterator.next()) {
            ITable table = iterator.getTable();
            ITableMetaData tableMetaData = table.getTableMetaData();
            String tableName = tableMetaData.getTableName();
            Column[] primaryKeys = tableMetaData.getPrimaryKeys();
            Column[] columns = tableMetaData.getColumns();
            System.out.println("tableName = " + tableName);
            System.out.println("primaryKeys = " + Arrays.toString(primaryKeys));
            System.out.println("columns = " + Arrays.toString(columns));
        }
    }
    
    @Test
    @DisplayName("Succeded on manual insertion / read by jpa.")
    void succededOnManualInsertionReadByJpa() throws Exception {
        // GIVEN
        MySQLFixture instance = MySQLFixture.getInstance();
        Connection dbConnection = DriverManager.getConnection(
            instance.getJdbcUrl(),
            instance.getUsername(),
            instance.getPassword()
        );
        ConnectionHolderImpl connectionHolder = new ConnectionHolderImpl(dbConnection);
        DataSetExecutorImpl dataSetExecutor = DataSetExecutorImpl.instance(connectionHolder);
        IDataSet iDataSet = dataSetExecutor.loadDataSet("mock/json/admin.json");
        RiderDataSource riderDataSource = dataSetExecutor.getRiderDataSource();
        DatabaseConnection dbUnitConnection = riderDataSource.getDBUnitConnection();
        DatabaseOperation.INSERT.execute(dbUnitConnection, iDataSet);

        // WHEN
        List<Admin> all = adminJpaRepository.findAll();

        // THEN
        assertFalse(all.isEmpty());
        all.forEach(admin -> {
            System.out.println("admin.getAdmIdx() : " + admin.getAdmIdx());
            System.out.println("admin.getAdmId() : " + admin.getAdmId());
            System.out.println("admin.getAdmPassword() : " + admin.getAdmPassword());
            System.out.println("admin.getUniqueId() : " + admin.getUniqueId());
            System.out.println("admin.getFullRoleName() : " + admin.getFullRoleName());
            System.out.println("admin.getCreatedAt() : " + admin.getCreatedAt());
            System.out.println("admin.getUpdatedAt() : " + admin.getUpdatedAt());
        });
    }

    @Test
    @DisplayName("Succeded on Spring Context's dependency over TestContainer datasource.")
    void succededOnTestContainerDataSourceVerfication() throws SQLException {
        // GIVEN
        MySQLFixture instance = MySQLFixture.getInstance();
        Connection dbConnection = DriverManager.getConnection(
            instance.getJdbcUrl(),
            instance.getUsername(),
            instance.getPassword()
        );

        // WHEN
        Connection connection = dataSource.getConnection();

        // THEN
        DatabaseMetaData metaData = connection.getMetaData();
        String url = metaData.getURL();
        String userName = metaData.getUserName();
        DatabaseMetaData metaData1 = dbConnection.getMetaData();
        String url1 = metaData1.getURL();
        String userName1 = metaData1.getUserName();
        assertTrue(url.contains(url1));
        assertEquals(userName, userName1);
    }
}

My remark on the main reason is on @DBRider annotation.

As you can see it's just a wrapper @TestExecutionListeners to run RiderRunner before the test code.

But for some reason, adding it breaks all of the assumption code above.

Is there any reason why it would not work ??

1 Answer 1

0

The core reason was that I've activated spring.datasource.hikari.auto-commit: false. The DBRider calls RiderRunner -- applies same whether you're using dbrider-spring, dbrider-core, dbrider-junit -- and activates sql commands through preparedstatement.

The thing is that when it comes to insert option, gets jdbc connection, calls preparedstatement and does not call a commit.

enter image description here

enter image description here

It seems like it's delegate commit to hikari, as ​HikariCP sets autoCommit to true for connections returned from the pool by default.

So if you turn auto-commit to false in hikari, dbrider insertion won't applied

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.