Testcontainers for Javaは、JUnitテストをサポートするJavaテストライブラリであり、Dockerコンテナ技術に基づいて軽量で使い捨てのデータベースインスタンスを提供します。OceanBaseモジュールを利用することで、OceanBase CEコンテナを自動的に起動して統合テストを実行でき、ユニットテストや統合テストなどのシナリオに適しています。
バージョン互換性
コンポーネント |
バージョン要件 |
|---|---|
| OceanBase CE | ≥ V4.2.1 |
| testcontainers-oceanbase | ≥ V2.0.4 |
前提条件
Testcontainersを使用する前に、以下のことを確認してください:
- JDK 11以降がインストールされていること。JDKのインストールについては、Oracle JDK公式ドキュメントを参照してください。
- Maven 3.6以降がインストールされていること。Mavenのインストールについては、Maven公式インストールガイドを参照してください。
- Dockerがインストールされ、起動していること。Dockerのインストールについては、Docker公式ドキュメントを参照してください。
- (オプション)既存のOceanBaseインスタンスに接続する場合は、OceanBaseデータベースのデプロイが完了し、MySQLモードのユーザーテナントが作成されていること。ユーザーテナントの作成方法の詳細については、テナントの作成を参照してください。
手順
ステップ1:Mavenプロジェクトを作成する
ローカルにMavenプロジェクトディレクトリを作成し、標準のディレクトリ構造を初期化します:
mkdir -p oceanbase-testcontainers-demo/src/main/java/com/example/oceanbase/demo mkdir -p oceanbase-testcontainers-demo/src/main/resources cd oceanbase-testcontainers-demoMaven設定ファイルを作成します。
プロジェクトのルートディレクトリに
pom.xmlファイルを作成し、以下の依存関係設定を記述します:<?xml version="1.0" encoding="UTF-8"?> <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> <groupId>com.example</groupId> <artifactId>oceanbase-testcontainers-demo</artifactId> <version>1.0.0-SNAPSHOT</version> <name>oceanbase-testcontainers-demo</name> <description>Demo: OceanBase CE (MySQL tenant) via Testcontainers for Java</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.release>11</maven.compiler.release> <testcontainers.version>2.0.4</testcontainers.version> </properties> <dependencies> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers-oceanbase</artifactId> <version>${testcontainers.version}</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.4.0</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.13</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <release>${maven.compiler.release}</release> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.5.0</version> <configuration> <mainClass>com.example.oceanbase.demo.OceanBaseDemo</mainClass> <cleanupDaemonThreads>false</cleanupDaemonThreads> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.4.2</version> <configuration> <archive> <manifest> <mainClass>com.example.oceanbase.demo.OceanBaseDemo</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> </project>ログ設定ファイルを作成します。
src/main/resources/ディレクトリにlogback.xmlファイルを追加し、コンソールログ出力形式と関連コンポーネントのログレベルを設定します。内容は以下のとおりです:<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="org.testcontainers" level="INFO"/> <logger name="com.github.dockerjava" level="WARN"/> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </configuration>サンプルプログラムを作成します。
src/main/java/com/example/oceanbase/demo/ディレクトリにOceanBaseDemo.javaファイルを追加し、テストプログラムの実装を示すために使用します。内容は以下のとおりです:package com.example.oceanbase.demo; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Ulimit; import org.testcontainers.oceanbase.OceanBaseCEContainer; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.time.Duration; import java.util.Collections; import java.util.Optional; /** * シングルエントリポイント:パラメータがない場合、Testcontainersを使用してOceanBase CEを起動し、データベースの作成/テーブルの作成/挿入/クエリを実行します。<br> * 3つのパラメータがある場合、既存のインスタンスに接続します:{@code <jdbcUrl> <user> <password>}. */ public final class OceanBaseDemo { private static final String IMAGE = "oceanbase/oceanbase-ce:4.2.1-lts"; static final String DEMO_DATABASE = "demo_db"; static final String DEMO_TABLE = "demo_orders"; public static void main(String[] args) { try { runMain(args); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { if (exceptionChainContainsInterrupted(e)) { Thread.currentThread().interrupt(); return; } e.printStackTrace(); System.exit(1); } } private static void runMain(String[] args) throws Exception { if (args.length >= 3) { try (Connection conn = DriverManager.getConnection(args[0], args[1], args[2])) { runCrud(conn); } return; } if (args.length != 0) { System.err.println("Usage:"); System.err.println(" (no args) Start OceanBase in Docker via Testcontainers, then run CRUD demo."); System.err.println(" <jdbcUrl> <user> <password> Connect to an existing OceanBase MySQL tenant."); System.exit(1); return; } OceanBaseCEContainer oceanbase = new OceanBaseCEContainer(IMAGE) .withPassword("integration-secret") .withUrlParam("useSSL", "false") .withStartupTimeout(Duration.ofMinutes(25)) .withSharedMemorySize(1024L * 1024 * 1024) .withCreateContainerCmdModifier(cmd -> { HostConfig hc = Optional.ofNullable(cmd.getHostConfig()).orElseGet(HostConfig::new); hc.withUlimits(Collections.singletonList(new Ulimit("nproc", 131072, 131072))); hc.withMemory(4L * 1024 * 1024 * 1024); cmd.withHostConfig(hc); }); oceanbase.start(); try (Connection conn = DriverManager.getConnection( oceanbase.getJdbcUrl(), oceanbase.getUsername(), oceanbase.getPassword())) { runCrud(conn); verifyRowCount(conn); } // oceanbase.stop()を明示的に呼び出さない:RyukはJVM終了時にクリーンアップします。execプラグインについては、pom.xml内のcleanupDaemonThreadsを参照してください。 } private static boolean exceptionChainContainsInterrupted(Throwable e) { for (Throwable t = e; t != null; t = t.getCause()) { if (t instanceof InterruptedException) { return true; } } return false; } static void runCrud(Connection conn) throws SQLException { conn.setAutoCommit(true); try (Statement st = conn.createStatement()) { st.executeUpdate("CREATE DATABASE IF NOT EXISTS " + DEMO_DATABASE); } try (Statement st = conn.createStatement()) { st.execute("USE " + DEMO_DATABASE); } try (Statement st = conn.createStatement()) { st.executeUpdate( "CREATE TABLE IF NOT EXISTS " + DEMO_TABLE + " (" + "id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY," + "product_name VARCHAR(128) NOT NULL," + "qty INT NOT NULL DEFAULT 1," + "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ")" ); } try (Statement st = conn.createStatement()) { st.executeUpdate("DELETE FROM " + DEMO_TABLE); int inserted = st.executeUpdate( "INSERT INTO " + DEMO_TABLE + " (product_name, qty) VALUES " + [('Notebook', 2), ('Pen', 10), ('OceanBase Guide', 1)] ); System.out.println("[CRUD demo] INSERT affected rows: " + inserted); } System.out.println("[CRUD demo] ---------- SELECT result ----------"); try ( Statement st = conn.createStatement(); ResultSet rs = st.executeQuery( "SELECT id, product_name, qty, created_at FROM " + DEMO_TABLE + " ORDER BY id" ) ) { int count = 0; while (rs.next()) { count++; System.out.println( "[CRUD demo] row " + count + " | id=" + rs.getLong("id") + " | product_name=" + rs.getString("product_name") + " | qty=" + rs.getInt("qty") + " | created_at=" + rs.getTimestamp("created_at") ); } System.out.println("[CRUD demo] ---------- total rows: " + count + " ----------"); } } private static void verifyRowCount(Connection conn) throws SQLException { try (Statement st = conn.createStatement()) { st.execute("USE " + DEMO_DATABASE); try (ResultSet rs = st.executeQuery("SELECT COUNT(*) AS c FROM " + DEMO_TABLE)) { if (!rs.next()) { throw new IllegalStateException("expected count row"); } int c = rs.getInt("c"); if (c != 3) { throw new IllegalStateException("expected 3 rows, got " + c); } } } } private OceanBaseDemo() {} }
ステップ2:コンパイルと検証
プロジェクトのルートディレクトリで以下のコマンドを実行し、サンプルプロジェクトをコンパイルします:
mvn -q compile
- 成功:コマンドが完了し、エラーがなく、
target/classesディレクトリが生成された場合。 - 失敗:コンソールログを確認し、JDK、Maven、ネットワーク環境、またはプライベートリポジトリの設定が正しいかどうか確認してください。
ステップ3:テストサンプルを実行する
Testcontainersは2つの実行モードを提供します:
モードA:パラメータなしモード(OceanBase CEコンテナの自動起動)
このモードは、ローカルにDockerがインストール済みで、1つのコマンドでイメージのプル、コンテナの起動、SQL実行のデモンストレーションを自動化したい場合に適しています。
プロジェクトのルートディレクトリで以下のコマンドを実行します:
mvn -q compile exec:java
初回実行時、プログラムは自動的にDocker Hubから oceanbase/oceanbase-ce:4.2.1-lts イメージをプルするため、数分待つ必要があります。 また、OceanBase CEコンテナが初めてコールドスタートする際、ログに boot success! が表示されるまでにも数分かかることがありますが、これは正常な現象です。
モードB:3パラメータモード(既存のOceanBaseインスタンスへの接続)
アクセス可能なOceanBase MySQLテナントが既に存在し、コンテナを起動する必要がない場合に適しています。
OceanBaseデータベースの接続文字列を取得します:OceanBaseデータベースのデプロイ担当者から接続文字列を取得します。例:
obclient -h$host -P$port -u$user_name -p$password -D$database_nameパラメータ説明:
$host:接続IP。ODP接続はODPアドレスを使用し、直接接続はOBServer IPを使用します。$port:接続ポート。ODPのデフォルトは2883、直接接続のデフォルトは2881です。$database_name:データベース名。注意
テナントに接続するユーザーには、データベースに対する
CREATE、INSERT、DROP、およびSELECT権限が付与されていなければなりません。ユーザー権限の詳細については、MySQLモードの権限分類を参照してください。$user_name:接続アカウント。ODP形式:ユーザー@テナント#クラスタまたはクラスタ:テナント:ユーザー、直接接続形式:ユーザー@テナント。$password:アカウントのパスワード。
接続文字列の詳細については、OBClientを使用してOceanBaseテナントに接続するを参照してください。
例:
obclient -hxxx.xxx.xxx.xxx -P2881 -utest_user001@mysql001 -p****** -Dtestテストプログラムを実行します:接続情報をJDBC URL形式に変換し、以下のコマンドを実行します。
mvn -q compile exec:java -Dexec.args="jdbc:mysql://$host:$port/$database_name?useSSL=false $user_name $password"$host、$port、$database_name、$user_name、$passwordを実際の値に置き換えてください。
結果の検証
実行が成功すると、コンソールには以下の内容が出力されます:
[CRUD demo] INSERT affected rows: 3
[CRUD demo] ---------- SELECT result ----------
[CRUD demo] row 1 | id=7 | product_name=Notebook | qty=2 | created_at=2026-04-01 16:57:06.0
[CRUD demo] row 2 | id=8 | product_name=Pen | qty=10 | created_at=2026-04-01 16:57:06.0
[CRUD demo] row 3 | id=9 | product_name=OceanBase Guide | qty=1 | created_at=2026-04-01 16:57:06.0
[CRUD demo] ---------- total rows: 3 ----------
よくある質問
Q1:OBD-2000 / not enough memory エラーが発生した場合はどうすればよいですか?
原因:Dockerの利用可能なメモリが不足しています。OceanBase CEコンテナの自己検査には、約3GB以上の利用可能なメモリが必要です。
解決策:Dockerで利用可能なメモリを増やします。他のメモリを消費するコンテナを閉じてから、再度試行してください。
Q2:max user processes / ulimit関連のエラーが発生した場合はどうすればよいですか?
原因:コンテナのプロセス数制限が低すぎます。
解決策:サンプルコードでは、コンテナに対して高いnproc値(131072)が設定されています。それでもエラーが発生する場合は、Dockerのバージョンとホスト機のリソースを確認してください。
Q3:Could not find artifact org.testcontainers エラーが発生した場合はどうすればよいですか?
原因:Mavenが依存関係をダウンロードできません。
解決策:Mavenのsettings.xml設定、プライベートリポジトリがCentralと同期されているかどうかを確認するか、外部ネットワークにアクセス可能なリポジトリを設定してください。
Q4:イメージのプルが非常に遅い、またはタイムアウトした場合はどうすればよいですか?
原因:ネットワークの問題により、Docker Hubへのアクセスが遅くなっています。
解決策:ネットワークを確認します。必要に応じて、イメージの高速化を設定するか、オフラインでイメージをインポートしてください。