or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

container-configuration.mdcontainer-customization.mdcontainer-lifecycle.mddatabase-initialization.mdindex.mdjdbc-connection.mdr2dbc-support.mdurl-parameters-timeouts.md
tile.json

jdbc-connection.mddocs/

JDBC Connection Management

This document covers obtaining JDBC connection information and creating database connections for testing with MySQL containers.

Quick Reference

Connection Information Methods:

  • getJdbcUrl(): String - Returns complete JDBC URL with host, port, database, and parameters
  • getDriverClassName(): String - Returns driver class name (auto-detects 5.x vs 8.x)
  • getUsername(): String - Returns username (default: "test")
  • getPassword(): String - Returns password (default: "test")
  • getDatabaseName(): String - Returns database name (default: "test")
  • getTestQueryString(): String - Returns test query used for liveness checks ("SELECT 1")

Connection Creation Methods:

  • createConnection(String queryString): Connection - Creates JDBC Connection (throws SQLException, NoDriverFoundException)
  • getJdbcDriverInstance(): Driver - Returns Driver instance (throws NoDriverFoundException)

Automatic JDBC URL Parameters:

  • useSSL=false - Added automatically if not present
  • allowPublicKeyRetrieval=true - Added automatically if not present

Important Notes:

  • All connection methods require container to be started (start() must be called first)
  • Connections must be explicitly closed (use try-with-resources)
  • Multiple connections can be created from the same container
  • Each connection is thread-safe per connection instance

Capabilities

JDBC URL Retrieval

Get the JDBC connection URL for the running MySQL container.

/**
 * Returns the JDBC connection URL for this MySQL container.
 * The URL includes the host, mapped port, database name, and automatic parameters
 * for secure testing (useSSL=false, allowPublicKeyRetrieval=true).
 *
 * This method can only be called after the container has been started.
 * Calling it before start() will return a URL with incorrect port information.
 *
 * Format: jdbc:mysql://host:port/database?useSSL=false&allowPublicKeyRetrieval=true[&urlParams]
 *
 * @return the JDBC URL string
 * @throws IllegalStateException if called before container is started
 */
String getJdbcUrl();

Usage Example:

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withDatabaseName("testdb");
mysql.start(); // Must start before calling getJdbcUrl()

String jdbcUrl = mysql.getJdbcUrl();
// Example: "jdbc:mysql://localhost:32768/testdb?useSSL=false&allowPublicKeyRetrieval=true"

URL Details:

  • The URL automatically includes the container's mapped port (dynamically assigned)
  • useSSL=false is added automatically if not specified (disables SSL for testing)
  • allowPublicKeyRetrieval=true is added automatically if not specified (enables public key retrieval for testing)
  • Additional URL parameters set via withUrlParam() are appended
  • Host is typically "localhost" but may differ in complex networking setups
  • Port is dynamically assigned to avoid conflicts

URL Format:

jdbc:mysql://[host]:[mappedPort]/[database]?useSSL=false&allowPublicKeyRetrieval=true[&additionalParams]

Common URL Examples:

  • Basic: jdbc:mysql://localhost:32768/test?useSSL=false&allowPublicKeyRetrieval=true
  • With custom params: jdbc:mysql://localhost:32768/testdb?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&allowMultiQueries=true

JDBC Driver Class Name

Get the JDBC driver class name, with automatic detection of legacy vs. modern drivers.

/**
 * Returns the JDBC driver class name for MySQL.
 * Automatically detects whether the modern (8.x) or legacy (5.x) driver is available.
 *
 * The detection happens at runtime by attempting to load driver classes from the classpath.
 * This method does not require the container to be started.
 *
 * Modern driver: com.mysql.cj.jdbc.Driver (MySQL Connector/J 8.x+)
 * Legacy driver: com.mysql.jdbc.Driver (MySQL Connector/J 5.x)
 *
 * @return the driver class name
 */
String getDriverClassName();

Usage Example:

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
// Container does not need to be started for getDriverClassName()

String driverClass = mysql.getDriverClassName();
// Returns "com.mysql.cj.jdbc.Driver" if modern driver is on classpath
// Returns "com.mysql.jdbc.Driver" if only legacy driver is on classpath

Driver Detection:

  • The method first attempts to load com.mysql.cj.jdbc.Driver (modern driver)
  • If not found, it falls back to com.mysql.jdbc.Driver (legacy driver)
  • This allows the module to work with both MySQL Connector/J versions
  • Detection happens at runtime based on classpath contents
  • Method does not require container to be started

Driver Compatibility:

  • Modern driver (8.x): com.mysql.cj.jdbc.Driver - Recommended for MySQL 8.0+
  • Legacy driver (5.x): com.mysql.jdbc.Driver - For older applications
  • Both drivers support MySQL 5.7 and 8.0+ servers
  • Modern driver provides better performance and features

Connection Credentials

Retrieve the database connection credentials.

/**
 * Returns the username for database connections.
 * Returns the default value "test" if not explicitly configured.
 *
 * @return the username (default: "test")
 */
String getUsername();

/**
 * Returns the password for database connections.
 * Returns the default value "test" if not explicitly configured.
 *
 * @return the password (default: "test")
 */
String getPassword();

/**
 * Returns the database name.
 * Returns the default value "test" if not explicitly configured.
 *
 * @return the database name (default: "test")
 */
String getDatabaseName();

Usage Example:

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withDatabaseName("mydb")
    .withUsername("myuser")
    .withPassword("mypass");
mysql.start();

String username = mysql.getUsername(); // "myuser"
String password = mysql.getPassword(); // "mypass"
String database = mysql.getDatabaseName(); // "mydb"

Credential Notes:

  • Credentials are set during container configuration
  • Default values: username="test", password="test", database="test"
  • Credentials cannot be changed after container creation
  • Credentials apply to the configured user and root user
  • These methods can be called before or after container is started

Create JDBC Connection

Create a JDBC connection to the containerized database.

/**
 * Creates a new JDBC connection to the MySQL container.
 * The connection is created using the container's JDBC URL, username, and password.
 *
 * This method requires the container to be started. It will throw SQLException if
 * called before start() or if the container is not ready.
 *
 * The connection must be explicitly closed. Use try-with-resources for automatic cleanup.
 * Multiple connections can be created from the same container instance.
 *
 * @param queryString optional query string parameters to append to the JDBC URL.
 *                    Must include '?' if provided. Can be empty string.
 *                    Example: "?allowMultiQueries=true" or ""
 * @return a JDBC Connection object
 * @throws SQLException if connection cannot be established (container not started,
 *         network issues, invalid credentials, timeout)
 * @throws NoDriverFoundException if MySQL JDBC driver is not on classpath
 * @throws IllegalStateException if called before container is started
 */
Connection createConnection(String queryString) throws SQLException;

Usage Example:

import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start(); // Must start before creating connection

// Create connection without additional parameters
try (Connection conn = mysql.createConnection("")) {
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT VERSION()");
    rs.next();
    String version = rs.getString(1);
    System.out.println("MySQL version: " + version);
}

// Create connection with additional parameters
try (Connection conn = mysql.createConnection("?allowMultiQueries=true")) {
    Statement stmt = conn.createStatement();
    stmt.execute("CREATE TABLE test (id INT); INSERT INTO test VALUES (1);");
}

Connection Details:

  • Connection uses container's JDBC URL, username, and password
  • Connection is not automatically closed (use try-with-resources)
  • Additional query string parameters are appended to the JDBC URL
  • Connection is thread-safe (can be used from multiple threads)
  • Connection must be closed explicitly or via try-with-resources
  • Multiple connections can be created from the same container

Query String Parameter Format:

  • Empty string: No additional parameters
  • "?param=value": Single parameter (include '?' prefix)
  • "?param1=value1&param2=value2": Multiple parameters
  • Parameters are URL-encoded automatically if necessary

Connection Lifecycle:

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start();

// Create and use connection
try (Connection conn = mysql.createConnection("")) {
    // Use connection
    Statement stmt = conn.createStatement();
    stmt.execute("SELECT 1");
} // Connection automatically closed here

// Container can still be used to create more connections
try (Connection conn2 = mysql.createConnection("")) {
    // Another connection
}

Get JDBC Driver Instance

Obtain the JDBC driver instance for the MySQL connection.

/**
 * Returns an instance of the MySQL JDBC driver.
 * The driver is loaded and cached on first access.
 *
 * This method does not require the container to be started, but the driver
 * must be on the classpath.
 *
 * @return a JDBC Driver instance
 * @throws NoDriverFoundException if the MySQL JDBC driver is not on the classpath
 */
Driver getJdbcDriverInstance() throws NoDriverFoundException;

Usage Example:

import java.sql.Driver;
import java.sql.Connection;
import java.util.Properties;

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
// Container does not need to be started for getJdbcDriverInstance()

Driver driver = mysql.getJdbcDriverInstance();
Properties props = new Properties();
props.setProperty("user", mysql.getUsername());
props.setProperty("password", mysql.getPassword());

mysql.start(); // Must start before creating connection
Connection conn = driver.connect(mysql.getJdbcUrl(), props);
// Use connection...
conn.close();

Driver Instance Notes:

  • Driver is loaded and cached on first access
  • Throws NoDriverFoundException if driver not on classpath
  • Driver instance can be used to create connections manually
  • Useful for custom connection creation scenarios
  • Method does not require container to be started

Get Test Query String

Get the SQL query used for container liveness checks.

/**
 * Returns the SQL query used to test if the database is ready and accessible.
 * This query is executed during container startup to verify the database is operational.
 *
 * This method does not require the container to be started.
 *
 * @return the test query string (always "SELECT 1" for MySQL)
 */
String getTestQueryString();

Usage Example:

import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
// Container does not need to be started

String testQuery = mysql.getTestQueryString();
// testQuery == "SELECT 1"

Note: This method is primarily used internally by Testcontainers for liveness checks, but can be useful if you need to know what query is used to verify database readiness.

Complete JDBC Usage Example

import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MySQLIntegrationTest {

    public void testDatabaseOperation() throws SQLException {
        try (MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
                .withDatabaseName("testdb")
                .withUsername("testuser")
                .withPassword("testpass")) {

            mysql.start();

            // Get connection details
            String jdbcUrl = mysql.getJdbcUrl();
            String username = mysql.getUsername();
            String password = mysql.getPassword();

            // Create connection and execute queries
            try (Connection conn = mysql.createConnection("")) {
                // Create table
                Statement stmt = conn.createStatement();
                stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100))");

                // Insert data
                stmt.execute("INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob')");

                // Query data
                ResultSet rs = stmt.executeQuery("SELECT * FROM users ORDER BY id");
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    System.out.println("User: " + id + " - " + name);
                }
            }
        }
    }
}

Using with Standard JDBC DriverManager

import java.sql.DriverManager;
import java.sql.Connection;

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start();

// Load driver class
Class.forName(mysql.getDriverClassName());

// Create connection using DriverManager
Connection conn = DriverManager.getConnection(
    mysql.getJdbcUrl(),
    mysql.getUsername(),
    mysql.getPassword()
);

// Use connection...
conn.close();
mysql.stop();

Using with Connection Pooling

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start();

HikariConfig config = new HikariConfig();
config.setJdbcUrl(mysql.getJdbcUrl());
config.setUsername(mysql.getUsername());
config.setPassword(mysql.getPassword());
config.setDriverClassName(mysql.getDriverClassName());
config.setMaximumPoolSize(10);

HikariDataSource dataSource = new HikariDataSource(config);

try (Connection conn = dataSource.getConnection()) {
    // Use connection from pool
}

dataSource.close();

Automatic JDBC URL Parameters

The MySQL module automatically adds the following parameters to JDBC URLs to facilitate secure testing:

  • useSSL=false: Disables SSL/TLS for connections (only added if not already present)
  • allowPublicKeyRetrieval=true: Allows retrieval of public keys from the server (only added if not already present)

These parameters are necessary for testing scenarios where SSL certificates are not configured and are automatically appended to the JDBC URL returned by getJdbcUrl().

Override Automatic Parameters:

// Override automatic SSL parameter
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUrlParam("useSSL", "true")
    .withUrlParam("trustCertificateKeyStoreUrl", "file:/path/to/keystore")
    .withUrlParam("trustCertificateKeyStorePassword", "password");

Error Handling

NoDriverFoundException: Thrown by getJdbcDriverInstance() or createConnection() if the MySQL JDBC driver is not found on the classpath. Ensure you have added a MySQL JDBC driver dependency:

<!-- Maven -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
    <scope>test</scope>
</dependency>
// Gradle
testImplementation 'com.mysql:mysql-connector-j:8.0.33'

SQLException: Thrown by createConnection() if the connection cannot be established within the configured timeout period. Common causes:

  • Container not started
  • Network connectivity issues
  • Invalid credentials
  • Database not ready
  • Connection timeout exceeded

Solution:

// Ensure container is started
mysql.start();

// Increase connection timeout
mysql.withConnectTimeoutSeconds(180);

// Check container logs
mysql.withLogConsumer(new Slf4jLogConsumer(logger));

IllegalStateException: Thrown by getJdbcUrl() or createConnection() if called before container is started.

Solution:

// Always start container before using connection methods
mysql.start();
String jdbcUrl = mysql.getJdbcUrl(); // Now safe to call

Best Practices

  1. Use Try-With-Resources: Always close connections to prevent leaks
  2. Get Connection Details After Start: Call getJdbcUrl() after start() to get correct port
  3. Handle Exceptions: Catch SQLException and NoDriverFoundException
  4. Use Connection Pooling: For multiple connections, use a connection pool
  5. Verify Driver Availability: Check for NoDriverFoundException early in tests
  6. Use Container Methods: Prefer createConnection() over manual DriverManager usage
  7. Close Connections Explicitly: Don't rely on garbage collection for connection cleanup
  8. Check Container State: Verify container is started before creating connections
  9. Use Appropriate Timeouts: Configure connection timeouts for slow environments
  10. Monitor Connection Usage: Be aware of connection limits and pool sizes