diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index abb8b0f7901..270f292dcf7 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -42,6 +42,7 @@ body:
- MySQL
- Neo4j
- NGINX
+ - OceanBase
- OpenFGA
- Oracle Free
- Oracle XE
diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml
index 54868eea96d..97297c9ddb2 100644
--- a/.github/ISSUE_TEMPLATE/enhancement.yaml
+++ b/.github/ISSUE_TEMPLATE/enhancement.yaml
@@ -42,6 +42,7 @@ body:
- MySQL
- Neo4j
- NGINX
+ - OceanBase
- OpenFGA
- Oracle Free
- Oracle XE
diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml
index 2d28aca91d6..80048f00fb0 100644
--- a/.github/ISSUE_TEMPLATE/feature.yaml
+++ b/.github/ISSUE_TEMPLATE/feature.yaml
@@ -42,6 +42,7 @@ body:
- MySQL
- Neo4j
- NGINX
+ - OceanBase
- OpenFGA
- Oracle Free
- Oracle XE
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ddf7a8b7d9a..22cbe2c2748 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -219,6 +219,11 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 10
+ - package-ecosystem: "gradle"
+ directory: "/modules/oceanbase"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/openfga"
schedule:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 8107870a59a..2b9dd3bc899 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -135,6 +135,10 @@
- changed-files:
- any-glob-to-any-file:
- modules/nginx/**/*
+"modules/oceanbase":
+ - changed-files:
+ - any-glob-to-any-file:
+ - modules/oceanbase/**/*
"modules/openfga":
- changed-files:
- any-glob-to-any-file:
diff --git a/.github/settings.yml b/.github/settings.yml
index ad711b60307..301fdcdacfd 100644
--- a/.github/settings.yml
+++ b/.github/settings.yml
@@ -193,6 +193,9 @@ labels:
- name: modules/nginx
color: '#006b75'
+ - name: modules/oceanbase
+ color: '#006b75'
+
- name: modules/openfga
color: '#006b75'
diff --git a/docs/modules/databases/jdbc.md b/docs/modules/databases/jdbc.md
index dc11954153a..c1d0171c59e 100644
--- a/docs/modules/databases/jdbc.md
+++ b/docs/modules/databases/jdbc.md
@@ -55,6 +55,10 @@ Insert `tc:` after `jdbc:` as follows. Note that the hostname, port and database
`jdbc:tc:sqlserver:2017-CU12:///databasename`
+#### Using OceanBase
+
+`jdbc:tc:oceanbasece:4.2.2:///databasename`
+
#### Using Oracle
`jdbc:tc:oracle:21-slim-faststart:///databasename`
diff --git a/docs/modules/databases/oceanbase.md b/docs/modules/databases/oceanbase.md
new file mode 100644
index 00000000000..b90a492a6e4
--- /dev/null
+++ b/docs/modules/databases/oceanbase.md
@@ -0,0 +1,25 @@
+# OceanBase Module
+
+See [Database containers](./index.md) for documentation and usage that is common to all relational database container types.
+
+## Adding this module to your project dependencies
+
+Add the following dependency to your `pom.xml`/`build.gradle` file:
+
+=== "Gradle"
+ ```groovy
+ testImplementation "org.testcontainers:oceanbase:{{latest_version}}"
+ ```
+
+=== "Maven"
+ ```xml
+
+ org.testcontainers
+ oceanbase
+ {{latest_version}}
+ test
+
+ ```
+
+!!! hint
+Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency.
diff --git a/mkdocs.yml b/mkdocs.yml
index 8c5a0a769f1..ce0c33dcf16 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -64,6 +64,7 @@ nav:
- modules/databases/mssqlserver.md
- modules/databases/mysql.md
- modules/databases/neo4j.md
+ - modules/databases/oceanbase.md
- modules/databases/oraclefree.md
- modules/databases/oraclexe.md
- modules/databases/orientdb.md
diff --git a/modules/oceanbase/build.gradle b/modules/oceanbase/build.gradle
new file mode 100644
index 00000000000..e1f13a7a6a1
--- /dev/null
+++ b/modules/oceanbase/build.gradle
@@ -0,0 +1,8 @@
+description = "Testcontainers :: JDBC :: OceanBase"
+
+dependencies {
+ api project(':jdbc')
+
+ testImplementation project(':jdbc-test')
+ testRuntimeOnly 'mysql:mysql-connector-java:8.0.33'
+}
diff --git a/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseCEContainer.java b/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseCEContainer.java
new file mode 100644
index 00000000000..6c08ff165f2
--- /dev/null
+++ b/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseCEContainer.java
@@ -0,0 +1,82 @@
+package org.testcontainers.oceanbase;
+
+import org.testcontainers.containers.JdbcDatabaseContainer;
+import org.testcontainers.utility.DockerImageName;
+
+/**
+ * Testcontainers implementation for OceanBase Community Edition.
+ *
+ * Supported image: {@code oceanbase/oceanbase-ce}
+ *
+ * Exposed ports:
+ *
+ * - SQL: 2881
+ * - RPC: 2882
+ *
+ */
+public class OceanBaseCEContainer extends JdbcDatabaseContainer {
+
+ static final String NAME = "oceanbasece";
+
+ static final String DOCKER_IMAGE_NAME = "oceanbase/oceanbase-ce";
+
+ private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(DOCKER_IMAGE_NAME);
+
+ private static final Integer SQL_PORT = 2881;
+
+ private static final Integer RPC_PORT = 2882;
+
+ private static final String DEFAULT_TEST_TENANT_NAME = "test";
+
+ private static final String DEFAULT_USERNAME = "root";
+
+ private static final String DEFAULT_PASSWORD = "";
+
+ private static final String DEFAULT_DATABASE_NAME = "test";
+
+ public OceanBaseCEContainer(String dockerImageName) {
+ this(DockerImageName.parse(dockerImageName));
+ }
+
+ public OceanBaseCEContainer(DockerImageName dockerImageName) {
+ super(dockerImageName);
+ dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
+
+ addExposedPorts(SQL_PORT, RPC_PORT);
+ }
+
+ @Override
+ public String getDriverClassName() {
+ return OceanBaseJdbcUtils.getDriverClass();
+ }
+
+ @Override
+ public String getJdbcUrl() {
+ String additionalUrlParams = constructUrlParameters("?", "&");
+ String prefix = OceanBaseJdbcUtils.isMySQLDriver(getDriverClassName()) ? "jdbc:mysql://" : "jdbc:oceanbase://";
+ return prefix + getHost() + ":" + getMappedPort(SQL_PORT) + "/" + DEFAULT_DATABASE_NAME + additionalUrlParams;
+ }
+
+ @Override
+ public String getDatabaseName() {
+ return DEFAULT_DATABASE_NAME;
+ }
+
+ @Override
+ public String getUsername() {
+ // In OceanBase, the jdbc username is related to the name of user, tenant and cluster,
+ // if a tenant name other than the default value 'test' is used, you should manually
+ // construct the jdbc username by yourself.
+ return DEFAULT_USERNAME + "@" + DEFAULT_TEST_TENANT_NAME;
+ }
+
+ @Override
+ public String getPassword() {
+ return DEFAULT_PASSWORD;
+ }
+
+ @Override
+ protected String getTestQueryString() {
+ return "SELECT 1";
+ }
+}
diff --git a/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseCEContainerProvider.java b/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseCEContainerProvider.java
new file mode 100644
index 00000000000..d0f651bc338
--- /dev/null
+++ b/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseCEContainerProvider.java
@@ -0,0 +1,32 @@
+package org.testcontainers.oceanbase;
+
+import org.testcontainers.containers.JdbcDatabaseContainer;
+import org.testcontainers.containers.JdbcDatabaseContainerProvider;
+import org.testcontainers.utility.DockerImageName;
+
+/**
+ * Factory for OceanBase Community Edition containers.
+ */
+public class OceanBaseCEContainerProvider extends JdbcDatabaseContainerProvider {
+
+ private static final String DEFAULT_TAG = "4.2.2";
+
+ @Override
+ public boolean supports(String databaseType) {
+ return databaseType.equals(OceanBaseCEContainer.NAME);
+ }
+
+ @Override
+ public JdbcDatabaseContainer newInstance() {
+ return newInstance(DEFAULT_TAG);
+ }
+
+ @Override
+ public JdbcDatabaseContainer newInstance(String tag) {
+ if (tag != null) {
+ return new OceanBaseCEContainer(DockerImageName.parse(OceanBaseCEContainer.DOCKER_IMAGE_NAME).withTag(tag));
+ } else {
+ return newInstance();
+ }
+ }
+}
diff --git a/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseJdbcUtils.java b/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseJdbcUtils.java
new file mode 100644
index 00000000000..d2e90f48ee6
--- /dev/null
+++ b/modules/oceanbase/src/main/java/org/testcontainers/oceanbase/OceanBaseJdbcUtils.java
@@ -0,0 +1,45 @@
+package org.testcontainers.oceanbase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utils for OceanBase Jdbc Connection.
+ */
+class OceanBaseJdbcUtils {
+
+ static final String MYSQL_JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
+
+ static final String MYSQL_LEGACY_JDBC_DRIVER = "com.mysql.jdbc.Driver";
+
+ static final String OCEANBASE_JDBC_DRIVER = "com.oceanbase.jdbc.Driver";
+
+ static final String OCEANBASE_LEGACY_JDBC_DRIVER = "com.alipay.oceanbase.jdbc.Driver";
+
+ static final List SUPPORTED_DRIVERS = Arrays.asList(
+ OCEANBASE_JDBC_DRIVER,
+ OCEANBASE_LEGACY_JDBC_DRIVER,
+ MYSQL_JDBC_DRIVER,
+ MYSQL_LEGACY_JDBC_DRIVER
+ );
+
+ static String getDriverClass() {
+ for (String driverClass : SUPPORTED_DRIVERS) {
+ try {
+ Class.forName(driverClass);
+ return driverClass;
+ } catch (ClassNotFoundException e) {
+ // try to load next driver
+ }
+ }
+ throw new RuntimeException("Can't find valid driver class for OceanBase");
+ }
+
+ static boolean isMySQLDriver(String driverClassName) {
+ return MYSQL_JDBC_DRIVER.equals(driverClassName) || MYSQL_LEGACY_JDBC_DRIVER.equals(driverClassName);
+ }
+
+ static boolean isOceanBaseDriver(String driverClassName) {
+ return OCEANBASE_JDBC_DRIVER.equals(driverClassName) || OCEANBASE_LEGACY_JDBC_DRIVER.equals(driverClassName);
+ }
+}
diff --git a/modules/oceanbase/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/modules/oceanbase/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider
new file mode 100644
index 00000000000..505bfe5e088
--- /dev/null
+++ b/modules/oceanbase/src/main/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider
@@ -0,0 +1 @@
+org.testcontainers.oceanbase.OceanBaseCEContainerProvider
diff --git a/modules/oceanbase/src/test/java/org/testcontainers/oceanbase/OceanBaseJdbcDriverTest.java b/modules/oceanbase/src/test/java/org/testcontainers/oceanbase/OceanBaseJdbcDriverTest.java
new file mode 100644
index 00000000000..a4f63b1eb90
--- /dev/null
+++ b/modules/oceanbase/src/test/java/org/testcontainers/oceanbase/OceanBaseJdbcDriverTest.java
@@ -0,0 +1,19 @@
+package org.testcontainers.oceanbase;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.testcontainers.jdbc.AbstractJDBCDriverTest;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+
+@RunWith(Parameterized.class)
+public class OceanBaseJdbcDriverTest extends AbstractJDBCDriverTest {
+
+ @Parameterized.Parameters(name = "{index} - {0}")
+ public static Iterable