diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index aee73cfa8..1f29ee133 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,6 +1,6 @@
[versions]
# Project versions
-nativeBuildTools = "0.9.16"
+nativeBuildTools = "0.9.18"
# External dependencies
junitPlatform = "1.8.1"
diff --git a/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/index.json b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/index.json
new file mode 100644
index 000000000..9512e972d
--- /dev/null
+++ b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/index.json
@@ -0,0 +1,7 @@
+[
+ "jni-config.json",
+ "proxy-config.json",
+ "reflect-config.json",
+ "resource-config.json",
+ "serialization-config.json"
+]
diff --git a/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/jni-config.json b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/jni-config.json
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/jni-config.json
@@ -0,0 +1 @@
+[]
diff --git a/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/proxy-config.json b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/proxy-config.json
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/proxy-config.json
@@ -0,0 +1 @@
+[]
diff --git a/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/reflect-config.json b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/reflect-config.json
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/reflect-config.json
@@ -0,0 +1 @@
+[]
diff --git a/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/resource-config.json b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/resource-config.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/resource-config.json
@@ -0,0 +1 @@
+{}
diff --git a/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/serialization-config.json b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/serialization-config.json
new file mode 100644
index 000000000..5b382b874
--- /dev/null
+++ b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/serialization-config.json
@@ -0,0 +1,4 @@
+{
+ "lambdaCapturingTypes": [],
+ "types": []
+}
diff --git a/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/index.json b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/index.json
new file mode 100644
index 000000000..bc15bda68
--- /dev/null
+++ b/metadata/com.baomidou/dynamic-datasource-spring-boot-starter/index.json
@@ -0,0 +1,10 @@
+[
+ {
+ "latest": true,
+ "metadata-version": "3.6.1",
+ "module": "com.baomidou:dynamic-datasource-spring-boot-starter",
+ "tested-versions": [
+ "3.6.1"
+ ]
+ }
+]
diff --git a/metadata/index.json b/metadata/index.json
index 742cfa29f..333d87b7e 100644
--- a/metadata/index.json
+++ b/metadata/index.json
@@ -201,5 +201,9 @@
{
"directory": "org.eclipse.jetty/jetty-client",
"module": "org.eclipse.jetty:jetty-client"
+ },
+ {
+ "directory": "com.baomidou/dynamic-datasource-spring-boot-starter",
+ "module": "com.baomidou:dynamic-datasource-spring-boot-starter"
}
]
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/.gitignore b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/.gitignore
new file mode 100644
index 000000000..c98c7875b
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/.gitignore
@@ -0,0 +1,4 @@
+gradlew.bat
+gradlew
+gradle/
+build/
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/build.gradle b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/build.gradle
new file mode 100644
index 000000000..2607d6ef7
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+
+plugins {
+ id "org.graalvm.internal.tck"
+ id 'org.springframework.boot' version '3.0.0'
+}
+
+String libraryVersion = tck.testedLibraryVersion.get()
+
+dependencies {
+ testImplementation "com.baomidou:dynamic-datasource-spring-boot-starter:$libraryVersion"
+ testImplementation 'org.assertj:assertj-core:3.22.0'
+ testImplementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
+ testImplementation 'org.springframework.boot:spring-boot-starter-web'
+ testRuntimeOnly 'com.h2database:h2:2.1.214'
+ testImplementation 'org.springframework.boot:spring-boot-starter-test'
+}
+
+group 'com_baomidou.dynamic_datasource_spring_boot_starter'
+
+graalvmNative {
+ agent {
+ defaultMode = "conditional"
+ modes {
+ conditional {
+ userCodeFilterPath = "user-code-filter.json"
+ }
+ }
+ metadataCopy {
+ mergeWithExisting = true
+ inputTaskNames.add("test")
+ outputDirectories.add("src/test/resources/META-INF/native-image/com.baomidou/dynamic-datasource-spring-boot-starter")
+ }
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/gradle.properties b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/gradle.properties
new file mode 100644
index 000000000..8403743d9
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/gradle.properties
@@ -0,0 +1,2 @@
+library.version = 3.6.1
+metadata.dir = com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/settings.gradle b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/settings.gradle
new file mode 100644
index 000000000..cf6b65fd7
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/settings.gradle
@@ -0,0 +1,13 @@
+pluginManagement {
+ def tckPath = Objects.requireNonNullElse(
+ System.getenv("GVM_TCK_TCKDIR"),
+ "../../../../tck-build-logic"
+ )
+ includeBuild(tckPath)
+}
+
+plugins {
+ id "org.graalvm.internal.tck-settings" version "1.0.0-SNAPSHOT"
+}
+
+rootProject.name = 'com.baomidou.dynamic-datasource-spring-boot-starter_tests'
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/AddRemoveDatasourceTest.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/AddRemoveDatasourceTest.java
new file mode 100644
index 000000000..51db7ce15
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/AddRemoveDatasourceTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter;
+
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.hikari.HikariCpConfig;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.sql.DataSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@SpringBootTest(classes = AddRemoveDatasourceApplication.class, webEnvironment = RANDOM_PORT)
+public class AddRemoveDatasourceTest {
+ @Autowired
+ DataSource dataSource;
+ @Autowired
+ DefaultDataSourceCreator dataSourceCreator;
+
+ @Test
+ void testAddAndRemoveDataSource() {
+ HikariCpConfig hikariCpConfig = new HikariCpConfig();
+ hikariCpConfig.setConnectionTestQuery("select 1");
+ DataSourceProperty dataSourceProperty = new DataSourceProperty()
+ .setPoolName("slave_1").setDriverClassName("org.h2.Driver").setUrl("jdbc:h2:mem:test1;MODE=MySQL")
+ .setUsername("sa").setPassword("").setHikari(hikariCpConfig);
+ DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
+ ds.addDataSource(dataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(dataSourceProperty));
+ assertThat(ds.getDataSources().keySet()).contains("slave_1");
+ ds.removeDataSource("slave_1");
+ assertThat(ds.getDataSources().keySet()).doesNotContain("slave_1");
+ }
+}
+
+@SpringBootApplication
+class AddRemoveDatasourceApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(AddRemoveDatasourceApplication.class, args);
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/LoadDatasourceFromJDBCTest.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/LoadDatasourceFromJDBCTest.java
new file mode 100644
index 000000000..77ec85a9d
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/LoadDatasourceFromJDBCTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter;
+
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.provider.AbstractJdbcDataSourceProvider;
+import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+
+import javax.sql.DataSource;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@SpringBootTest(classes = LoadDatasourceFromJDBCApplication.class, webEnvironment = RANDOM_PORT)
+public class LoadDatasourceFromJDBCTest {
+ @Autowired
+ DataSource dataSource;
+
+ @Test
+ void testExistDataSource() {
+ DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
+ assertThat(ds.getDataSources().keySet()).contains("master", "db1", "db2", "db3");
+ }
+}
+
+@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"})
+@SpringBootApplication
+class LoadDatasourceFromJDBCApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(LoadDatasourceFromJDBCApplication.class, args);
+ }
+
+ @Bean
+ public DynamicDataSourceProvider dynamicDataSourceProvider() {
+ return new AbstractJdbcDataSourceProvider("org.h2.Driver", "jdbc:h2:mem:test;MODE=MySQL", "sa", "") {
+ @Override
+ protected Map executeStmt(Statement statement) throws SQLException {
+ statement.execute("""
+ CREATE TABLE IF NOT EXISTS `DB`
+ (
+ `name` VARCHAR(30) NULL DEFAULT NULL,
+ `username` VARCHAR(30) NULL DEFAULT NULL,
+ `password` VARCHAR(30) NULL DEFAULT NULL,
+ `url` VARCHAR(30) NULL DEFAULT NULL,
+ `driver` VARCHAR(30) NULL DEFAULT NULL
+ )""");
+ statement.executeUpdate("insert into DB values ('master','sa','','jdbc:h2:mem:test;MODE=MySQL','org.h2.Driver')");
+ statement.executeUpdate("insert into DB values ('db1','sa','','jdbc:h2:mem:test2','org.h2.Driver')");
+ statement.executeUpdate("insert into DB values ('db2','sa','','jdbc:h2:mem:test3','org.h2.Driver')");
+ statement.executeUpdate("insert into DB values ('db3','sa','','jdbc:h2:mem:test4','org.h2.Driver')");
+ Map map = new HashMap<>();
+ ResultSet rs = statement.executeQuery("select * from DB");
+ while (rs.next()) {
+ map.put(rs.getString("name"), new DataSourceProperty()
+ .setUsername(rs.getString("username")).setPassword(rs.getString("password"))
+ .setUrl(rs.getString("url")).setDriverClassName(rs.getString("driver")));
+ }
+ return map;
+ }
+ };
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/NestDataSourceTest.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/NestDataSourceTest.java
new file mode 100644
index 000000000..9091aacbc
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/NestDataSourceTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter;
+
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import com_baomidou.dynamic_datasource_spring_boot_starter.service.nest.*;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.sql.DataSource;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@SpringBootTest(classes = NestApplication.class, webEnvironment = RANDOM_PORT)
+public class NestDataSourceTest {
+ @Autowired
+ DataSource dataSource;
+ @Autowired
+ DefaultDataSourceCreator dataSourceCreator;
+ @Autowired
+ private TeacherService teacherService;
+ @Autowired
+ private StudentService studentService;
+ @Autowired
+ private SchoolService schoolService;
+
+ @Test
+ void testNest() {
+ DataSourceProperty masterDataSourceProperty = new DataSourceProperty()
+ .setPoolName("master").setDriverClassName("org.h2.Driver")
+ .setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/add-remove-datasource.sql'")
+ .setUsername("sa").setPassword("");
+ DataSourceProperty teacherDataSourceProperty = new DataSourceProperty()
+ .setPoolName("teacher").setDriverClassName("org.h2.Driver").setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE")
+ .setUsername("sa").setPassword("");
+ DataSourceProperty studentDataSourceProperty = new DataSourceProperty()
+ .setPoolName("student").setDriverClassName("org.h2.Driver").setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE")
+ .setUsername("sa").setPassword("");
+ DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
+ ds.addDataSource(masterDataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(masterDataSourceProperty));
+ ds.addDataSource(teacherDataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(teacherDataSourceProperty));
+ ds.addDataSource(studentDataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(studentDataSourceProperty));
+ assertThat(ds.getDataSources().keySet()).contains("master", "teacher", "student");
+ assertThat(teacherService.addTeacherWithTx("ss", 1)).isEqualTo(1);
+ assertThat(studentService.addStudentWithTx("tt", 2)).isEqualTo(1);
+ assertThat(teacherService.selectTeachers()).isEqualTo(List.of(new Teacher(1, "tt", 2)));
+ assertThat(studentService.selectStudents()).isEqualTo(List.of(new Student(1, "tt", 2)));
+ assertThat(schoolService.addTeacherAndStudentWithTx()).isEqualTo(2);
+ assertThat(teacherService.selectTeachers()).isEqualTo(List.of(new Teacher(1, "tt", 2), new Teacher(2, "bb", 4)));
+ assertThat(studentService.selectStudents()).isEqualTo(List.of(new Student(1, "tt", 2), new Student(2, "bb", 4)));
+ }
+}
+
+@SpringBootApplication
+class NestApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(NestApplication.class, args);
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/SPELTest.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/SPELTest.java
new file mode 100644
index 000000000..f1812c456
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/SPELTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter;
+
+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
+import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
+import com_baomidou.dynamic_datasource_spring_boot_starter.service.spel.User;
+import com_baomidou.dynamic_datasource_spring_boot_starter.service.spel.UserService;
+import jakarta.servlet.ServletException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import javax.sql.DataSource;
+import java.nio.charset.StandardCharsets;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
+
+@SpringBootTest(classes = SPELApplication.class, webEnvironment = RANDOM_PORT)
+public class SPELTest {
+ MockMvc mockMvc;
+ @Autowired
+ DataSource dataSource;
+ @Autowired
+ DefaultDataSourceCreator dataSourceCreator;
+
+ @Autowired
+ UserService userService;
+
+ @BeforeEach
+ void setup(WebApplicationContext webApplicationContext) {
+ this.mockMvc = webAppContextSetup(webApplicationContext).defaultResponseCharacterEncoding(StandardCharsets.UTF_8).build();
+ }
+
+ @Test
+ void testSPEL() {
+ DataSourceProperty masterDataSourceProperty = new DataSourceProperty()
+ .setPoolName("master").setDriverClassName("org.h2.Driver")
+ .setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/spring-expression-language.sql'")
+ .setUsername("sa").setPassword("");
+ DataSourceProperty tenant1_1DataSourceProperty = new DataSourceProperty()
+ .setPoolName("tenant1_1").setDriverClassName("org.h2.Driver").setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE")
+ .setUsername("sa").setPassword("");
+ DataSourceProperty tenant1_2DataSourceProperty = new DataSourceProperty()
+ .setPoolName("tenant1_2").setDriverClassName("org.h2.Driver").setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE")
+ .setUsername("sa").setPassword("");
+ DataSourceProperty tenant2_1DataSourceProperty = new DataSourceProperty()
+ .setPoolName("tenant2_1").setDriverClassName("org.h2.Driver").setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE")
+ .setUsername("sa").setPassword("");
+ DataSourceProperty tenant2_2DataSourceProperty = new DataSourceProperty()
+ .setPoolName("tenant2_2").setDriverClassName("org.h2.Driver").setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE")
+ .setUsername("sa").setPassword("");
+ DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
+ ds.addDataSource(masterDataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(masterDataSourceProperty));
+ ds.addDataSource(tenant1_1DataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(tenant1_1DataSourceProperty));
+ ds.addDataSource(tenant1_2DataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(tenant1_2DataSourceProperty));
+ ds.addDataSource(tenant2_1DataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(tenant2_1DataSourceProperty));
+ ds.addDataSource(tenant2_2DataSourceProperty.getPoolName(), dataSourceCreator.createDataSource(tenant2_2DataSourceProperty));
+ assertThat(ds.getDataSources().keySet()).contains("master", "tenant1_1", "tenant1_2", "tenant2_1", "tenant2_2");
+ assertThrows(ServletException.class, () -> {
+ mockMvc.perform(MockMvcRequestBuilders.get("/users/session").characterEncoding(StandardCharsets.UTF_8))
+ .andDo(print()).andExpectAll(
+ status().isOk(),
+ content().encoding(StandardCharsets.UTF_8)
+ ).andReturn().getResponse().getContentAsString();
+ mockMvc.perform(MockMvcRequestBuilders.get("/users/header").contentType(MediaType.APPLICATION_FORM_URLENCODED)
+ .header("tenantName", "tenant1")
+ .characterEncoding(StandardCharsets.UTF_8)
+ ).andDo(print()).andExpectAll(
+ status().isOk(),
+ content().encoding(StandardCharsets.UTF_8)
+ ).andReturn().getResponse().getContentAsString();
+ });
+ assertThat(userService.selectSpelByKey("tenant1")).isEqualTo("tenant1");
+ assertThat(userService.selecSpelByTenant(new User("tenant2"))).isEqualTo("tenant2");
+ }
+}
+
+@SpringBootApplication
+class SPELApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(SPELApplication.class, args);
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/controller/UserController.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/controller/UserController.java
new file mode 100644
index 000000000..e275047db
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/controller/UserController.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.controller;
+
+import com_baomidou.dynamic_datasource_spring_boot_starter.service.spel.User;
+import com_baomidou.dynamic_datasource_spring_boot_starter.service.spel.UserService;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RequestMapping("/users")
+@RestController
+public class UserController {
+
+ @Autowired
+ private UserService userService;
+
+ @GetMapping("/session")
+ public List session(HttpServletRequest request) {
+ request.getSession().setAttribute("tenantName", "tenant1");
+ return userService.selectSpelBySession();
+ }
+
+ @GetMapping("/header")
+ public String header() {
+ userService.selectSpelByHeader();
+ return "success";
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/SchoolService.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/SchoolService.java
new file mode 100644
index 000000000..c398d0c54
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/SchoolService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.service.nest;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class SchoolService {
+ private final TeacherService teacherService;
+ private final StudentService studentService;
+
+ public SchoolService(TeacherService teacherService, StudentService studentService) {
+ this.teacherService = teacherService;
+ this.studentService = studentService;
+ }
+
+ @Transactional
+ public int addTeacherAndStudentWithTx() {
+ int aa = teacherService.addTeacherNoTx("aa", 3);
+ int bb = studentService.addStudentNoTx("bb", 4);
+ return aa + bb;
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/Student.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/Student.java
new file mode 100644
index 000000000..ed4cfbfed
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/Student.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.service.nest;
+
+public record Student(Integer id, String name, Integer age) {
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/StudentService.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/StudentService.java
new file mode 100644
index 000000000..2b7d10b37
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/StudentService.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.service.nest;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.sql.DataSource;
+import java.sql.*;
+import java.util.LinkedList;
+import java.util.List;
+
+@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"})
+@Service
+@DS("student")
+public class StudentService {
+ private final DataSource dataSource;
+
+ public StudentService(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ @Transactional
+ public int addStudentWithTx(String name, Integer age) {
+ try (Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement("insert into student (name,age) values (?,?)")) {
+ preparedStatement.setString(1, name);
+ preparedStatement.setInt(2, age);
+ return preparedStatement.executeUpdate();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int addStudentNoTx(String name, Integer age) {
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement preparedStatement = connection.prepareStatement("insert into student (name,age) values (?,?)")) {
+ preparedStatement.setString(1, name);
+ preparedStatement.setInt(2, age);
+ return preparedStatement.executeUpdate();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public List selectStudents() {
+ List result = new LinkedList<>();
+ try (Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement()) {
+ ResultSet resultSet = statement.executeQuery("SELECT * FROM student");
+ while (resultSet.next()) {
+ result.add(new Student(resultSet.getInt(1), resultSet.getString(2), resultSet.getInt(3)));
+ }
+ return result;
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/Teacher.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/Teacher.java
new file mode 100644
index 000000000..65075e300
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/Teacher.java
@@ -0,0 +1,10 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.service.nest;
+
+public record Teacher(Integer id, String name, Integer age) {
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/TeacherService.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/TeacherService.java
new file mode 100644
index 000000000..dc4f2e92f
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/nest/TeacherService.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.service.nest;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.sql.DataSource;
+import java.sql.*;
+import java.util.LinkedList;
+import java.util.List;
+
+@SuppressWarnings({"SqlNoDataSourceInspection", "SqlDialectInspection"})
+@Service
+@DS("teacher")
+public class TeacherService {
+ private final DataSource dataSource;
+
+ public TeacherService(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ @Transactional
+ public int addTeacherWithTx(String name, Integer age) {
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name,age) values (?,?)")) {
+ preparedStatement.setString(1, name);
+ preparedStatement.setInt(2, age);
+ return preparedStatement.executeUpdate();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ public int addTeacherNoTx(String name, Integer age) {
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement preparedStatement = connection.prepareStatement("insert into teacher (name,age) values (?,?)")) {
+ preparedStatement.setString(1, name);
+ preparedStatement.setInt(2, age);
+ return preparedStatement.executeUpdate();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public List selectTeachers() {
+ List result = new LinkedList<>();
+ try (Connection connection = dataSource.getConnection();
+ Statement statement = connection.createStatement()) {
+ ResultSet resultSet = statement.executeQuery("SELECT * FROM student");
+ while (resultSet.next()) {
+ result.add(new Teacher(resultSet.getInt(1), resultSet.getString(2), resultSet.getInt(3)));
+ }
+ return result;
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/spel/User.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/spel/User.java
new file mode 100644
index 000000000..e2392e450
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/spel/User.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.service.spel;
+
+public record User(Integer id, String name, Integer age, String tenantName) {
+ public User(String tenantName) {
+ this(null, null, null, tenantName);
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/spel/UserService.java b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/spel/UserService.java
new file mode 100644
index 000000000..a509c18a2
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/src/test/java/com_baomidou/dynamic_datasource_spring_boot_starter/service/spel/UserService.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright and related rights waived via CC0
+ *
+ * You should have received a copy of the CC0 legalcode along with this
+ * work. If not, see .
+ */
+package com_baomidou.dynamic_datasource_spring_boot_starter.service.spel;
+
+import com.baomidou.dynamic.datasource.annotation.DS;
+import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.LinkedList;
+import java.util.List;
+
+@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection", "UnusedReturnValue", "unused"})
+@Service
+@DS("slave")
+public class UserService {
+
+ private final DataSource dataSource;
+
+ public UserService(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+
+ @DS("#session.tenantName")
+ public List selectSpelBySession() {
+ List result = new LinkedList<>();
+ try (Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement()) {
+ ResultSet resultSet = statement.executeQuery("select * from t_user");
+ while (resultSet.next()) {
+ result.add(new User(resultSet.getInt(1), resultSet.getString(2), resultSet.getInt(3), resultSet.getString(4)));
+ }
+ return result;
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @DS("#header.tenantName")
+ public List selectSpelByHeader() {
+ List result = new LinkedList<>();
+ try (Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement()) {
+ ResultSet resultSet = statement.executeQuery("select * from t_user");
+ while (resultSet.next()) {
+ result.add(new User(resultSet.getInt(1), resultSet.getString(2), resultSet.getInt(3), resultSet.getString(4)));
+ }
+ return result;
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @DS("#tenantName")
+ public String selectSpelByKey(String tenantName) {
+ return DynamicDataSourceContextHolder.peek();
+
+ }
+
+ @DS("#user.tenantName")
+ public String selecSpelByTenant(User user) {
+ return DynamicDataSourceContextHolder.peek();
+ }
+}
diff --git a/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/user-code-filter.json b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/user-code-filter.json
new file mode 100644
index 000000000..9d9a98678
--- /dev/null
+++ b/tests/src/com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1/user-code-filter.json
@@ -0,0 +1,6 @@
+{
+ "rules": [
+ {"excludeClasses": "**"},
+ {"includeClasses": "com.baomidou.dynamic.datasource.**"}
+ ]
+}
diff --git a/tests/src/index.json b/tests/src/index.json
index d4d2495d1..9da21c02f 100644
--- a/tests/src/index.json
+++ b/tests/src/index.json
@@ -548,5 +548,16 @@
]
}
]
+ },
+ {
+ "test-project-path": "com.baomidou/dynamic-datasource-spring-boot-starter/3.6.1",
+ "libraries": [
+ {
+ "name": "com.baomidou:dynamic-datasource-spring-boot-starter",
+ "versions": [
+ "3.6.1"
+ ]
+ }
+ ]
}
]