From 45b2c8bbc2efe2012f86bbe80e935baf1143361d Mon Sep 17 00:00:00 2001 From: linghengqian Date: Mon, 21 Aug 2023 22:22:24 +0800 Subject: [PATCH] Add unit tests for `dynamic-datasource-spring-boot3-starter` --- .github/workflows/ci.yml | 67 +++++++++ README.md | 2 +- .../pom.xml | 13 +- .../fixture/AddRemoveDatasourceTest.java | 68 ++++++++++ .../fixture/LoadDatasourceFromJDBCTest.java | 95 +++++++++++++ .../fixture/NestDataSourceTest.java | 95 +++++++++++++ .../dynamic/datasource/fixture/SPELTest.java | 127 ++++++++++++++++++ .../fixture/controller/UserController.java | 46 +++++++ .../fixture/service/nest/SchoolService.java | 37 +++++ .../fixture/service/nest/Student.java | 19 +++ .../fixture/service/nest/StudentService.java | 75 +++++++++++ .../fixture/service/nest/Teacher.java | 19 +++ .../fixture/service/nest/TeacherService.java | 78 +++++++++++ .../datasource/fixture/service/spel/User.java | 22 +++ .../fixture/service/spel/UserService.java | 80 +++++++++++ .../resources/db/add-remove-datasource.sql | 15 +++ .../db/spring-expression-language.sql | 7 + pom.xml | 6 + 18 files changed, 869 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/AddRemoveDatasourceTest.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/LoadDatasourceFromJDBCTest.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/NestDataSourceTest.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/SPELTest.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/controller/UserController.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/SchoolService.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Student.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/StudentService.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Teacher.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/TeacherService.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/User.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/UserService.java create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/resources/db/add-remove-datasource.sql create mode 100644 dynamic-datasource-spring-boot3-starter/src/test/resources/db/spring-expression-language.sql diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..bcae8196 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: CI + +on: + push: + branches: [ master ] + paths: + - '.github/workflows/ci.yml' + - '**/pom.xml' + - '**/src/main/**' + - '**/src/test/**' + pull_request: + branches: [ master ] + paths: + - '.github/workflows/ci.yml' + - '**/pom.xml' + - '**/src/main/**' + - '**/src/test/**' + +concurrency: + group: ${{ github.event_name }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + MAVEN_OPTS: -Dhttps.protocols=TLSv1.2 -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true + +jobs: + test-maximum-jdk-ci: + name: Test CI - JDK ${{ matrix.java-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + os: [ ubuntu-latest ] + java-version: [ '17' ] + steps: + - uses: actions/checkout@v3 + - name: Setup java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{ matrix.java-version }} + cache: 'maven' + - name: Build test with Maven + run: | + ./mvnw -am -pl dynamic-datasource-spring-boot3-starter -T1C -B clean test + test-minimum-jdk-ci: + name: Test CI - JDK ${{ matrix.java-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + os: [ ubuntu-latest ] + java-version: [ '8' ] + steps: + - uses: actions/checkout@v3 + - name: Setup java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{ matrix.java-version }} + cache: 'maven' + - name: Build test with Maven + run: | + ./mvnw -am -pl dynamic-datasource-creator -T1C -B clean package + ./mvnw -am -pl dynamic-datasource-spring -T1C -B clean package + ./mvnw -am -pl dynamic-datasource-spring-boot-common -T1C -B clean package + ./mvnw -am -pl dynamic-datasource-spring-boot-starter -T1C -B clean package diff --git a/README.md b/README.md index 113a4fba..eae5864e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ - + diff --git a/dynamic-datasource-spring-boot3-starter/pom.xml b/dynamic-datasource-spring-boot3-starter/pom.xml index a6a65bba..072677c3 100644 --- a/dynamic-datasource-spring-boot3-starter/pom.xml +++ b/dynamic-datasource-spring-boot3-starter/pom.xml @@ -16,7 +16,7 @@ - 11 + 17 3.1.1 @@ -41,6 +41,17 @@ spring-boot-configuration-processor true + + com.h2database + h2 + ${h2.version} + test + + + org.springframework.boot + spring-boot-starter-test + test + \ No newline at end of file diff --git a/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/AddRemoveDatasourceTest.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/AddRemoveDatasourceTest.java new file mode 100644 index 00000000..384317b9 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/AddRemoveDatasourceTest.java @@ -0,0 +1,68 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture; + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.creator.DataSourceProperty; +import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; +import com.baomidou.dynamic.datasource.creator.hikaricp.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 org.springframework.jdbc.datasource.SimpleDriverDataSource; + +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(); + dataSourceProperty.setHikari(hikariCpConfig); + dataSourceProperty.setPoolName("slave_1"); + dataSourceProperty.setUsername("sa"); + dataSourceProperty.setPassword(""); + dataSourceProperty.setType(SimpleDriverDataSource.class); + dataSourceProperty.setUrl("jdbc:h2:mem:test1;MODE=MySQL"); + dataSourceProperty.setDriverClassName("org.h2.Driver"); + 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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/LoadDatasourceFromJDBCTest.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/LoadDatasourceFromJDBCTest.java new file mode 100644 index 00000000..b87e2ea7 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/LoadDatasourceFromJDBCTest.java @@ -0,0 +1,95 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture; + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.creator.DataSourceProperty; +import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; +import com.baomidou.dynamic.datasource.provider.AbstractJdbcDataSourceProvider; +import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider; +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 org.springframework.jdbc.datasource.SimpleDriverDataSource; + +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(DefaultDataSourceCreator dataSourceCreator) { + return new AbstractJdbcDataSourceProvider(dataSourceCreator, "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()) { + DataSourceProperty dataSourceProperty = new DataSourceProperty(); + dataSourceProperty.setUsername(rs.getString("username")); + dataSourceProperty.setPassword(rs.getString("password")); + dataSourceProperty.setUrl(rs.getString("url")); + dataSourceProperty.setDriverClassName(rs.getString("driver")); + dataSourceProperty.setType(SimpleDriverDataSource.class); + map.put(rs.getString("name"), dataSourceProperty); + } + return map; + } + }; + } +} diff --git a/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/NestDataSourceTest.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/NestDataSourceTest.java new file mode 100644 index 00000000..a052ffe6 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/NestDataSourceTest.java @@ -0,0 +1,95 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture; + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.creator.DataSourceProperty; +import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; +import com.baomidou.dynamic.datasource.fixture.service.nest.*; +import org.h2.tools.Server; +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.SQLException; +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 + TeacherService teacherService; + + @Autowired + StudentService studentService; + + @Autowired + SchoolService schoolService; + + @Test + void testNest() { + DataSourceProperty masterDataSourceProperty = new DataSourceProperty(); + masterDataSourceProperty.setPoolName("master"); + masterDataSourceProperty.setDriverClassName("org.h2.Driver"); + masterDataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/add-remove-datasource.sql'"); + masterDataSourceProperty.setUsername("sa"); + masterDataSourceProperty.setPassword(""); + DataSourceProperty teacherDataSourceProperty = new DataSourceProperty(); + teacherDataSourceProperty.setPoolName("teacher"); + teacherDataSourceProperty.setDriverClassName("org.h2.Driver"); + teacherDataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/add-remove-datasource.sql'"); + teacherDataSourceProperty.setUsername("sa"); + teacherDataSourceProperty.setPassword(""); + DataSourceProperty studentDataSourceProperty = new DataSourceProperty(); + studentDataSourceProperty.setPoolName("student"); + studentDataSourceProperty.setDriverClassName("org.h2.Driver"); + studentDataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/add-remove-datasource.sql'"); + studentDataSourceProperty.setUsername("sa"); + studentDataSourceProperty.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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/SPELTest.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/SPELTest.java new file mode 100644 index 00000000..490c10a0 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/SPELTest.java @@ -0,0 +1,127 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture; + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.creator.DataSourceProperty; +import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; +import com.baomidou.dynamic.datasource.fixture.service.spel.User; +import com.baomidou.dynamic.datasource.fixture.service.spel.UserService; +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.assertDoesNotThrow; +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(); + masterDataSourceProperty.setPoolName("master"); + masterDataSourceProperty.setDriverClassName("org.h2.Driver"); + masterDataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/spring-expression-language.sql'"); + masterDataSourceProperty.setUsername("sa"); + masterDataSourceProperty.setPassword(""); + DataSourceProperty tenant1_1DataSourceProperty = new DataSourceProperty(); + tenant1_1DataSourceProperty.setPoolName("tenant1_1"); + tenant1_1DataSourceProperty.setDriverClassName("org.h2.Driver"); + tenant1_1DataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/spring-expression-language.sql'"); + tenant1_1DataSourceProperty.setUsername("sa"); + tenant1_1DataSourceProperty.setPassword(""); + DataSourceProperty tenant1_2DataSourceProperty = new DataSourceProperty(); + tenant1_2DataSourceProperty.setPoolName("tenant1_2"); + tenant1_2DataSourceProperty.setDriverClassName("org.h2.Driver"); + tenant1_2DataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/spring-expression-language.sql'"); + tenant1_2DataSourceProperty.setUsername("sa"); + tenant1_2DataSourceProperty.setPassword(""); + DataSourceProperty tenant2_1DataSourceProperty = new DataSourceProperty(); + tenant2_1DataSourceProperty.setPoolName("tenant2_1"); + tenant2_1DataSourceProperty.setDriverClassName("org.h2.Driver"); + tenant2_1DataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM 'classpath:db/spring-expression-language.sql'"); + tenant2_1DataSourceProperty.setUsername("sa"); + tenant2_1DataSourceProperty.setPassword(""); + DataSourceProperty tenant2_2DataSourceProperty = new DataSourceProperty(); + tenant2_2DataSourceProperty.setPoolName("tenant2_2"); + tenant2_2DataSourceProperty.setDriverClassName("org.h2.Driver"); + tenant2_2DataSourceProperty.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE"); + tenant2_2DataSourceProperty.setUsername("sa"); + tenant2_2DataSourceProperty.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"); + assertDoesNotThrow(() -> { + 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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/controller/UserController.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/controller/UserController.java new file mode 100644 index 00000000..ca87dd69 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/controller/UserController.java @@ -0,0 +1,46 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.controller; + +import com.baomidou.dynamic.datasource.fixture.service.spel.User; +import com.baomidou.dynamic.datasource.fixture.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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/SchoolService.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/SchoolService.java new file mode 100644 index 00000000..3608baba --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/SchoolService.java @@ -0,0 +1,37 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Student.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Student.java new file mode 100644 index 00000000..3af25b22 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Student.java @@ -0,0 +1,19 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.service.nest; + +public record Student(Integer id, String name, Integer age) { +} diff --git a/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/StudentService.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/StudentService.java new file mode 100644 index 00000000..53c4815b --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/StudentService.java @@ -0,0 +1,75 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.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.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Teacher.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Teacher.java new file mode 100644 index 00000000..4a6d014d --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/Teacher.java @@ -0,0 +1,19 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.service.nest; + +public record Teacher(Integer id, String name, Integer age) { +} diff --git a/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/TeacherService.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/TeacherService.java new file mode 100644 index 00000000..dda872f6 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/nest/TeacherService.java @@ -0,0 +1,78 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.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.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/User.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/User.java new file mode 100644 index 00000000..c4b59f33 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/User.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.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/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/UserService.java b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/UserService.java new file mode 100644 index 00000000..b00d4965 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/java/com/baomidou/dynamic/datasource/fixture/service/spel/UserService.java @@ -0,0 +1,80 @@ +/* + * Copyright © 2018 organization baomidou + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.baomidou.dynamic.datasource.fixture.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/dynamic-datasource-spring-boot3-starter/src/test/resources/db/add-remove-datasource.sql b/dynamic-datasource-spring-boot3-starter/src/test/resources/db/add-remove-datasource.sql new file mode 100644 index 00000000..97e13ac6 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/resources/db/add-remove-datasource.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS TEACHER +( + id BIGINT(20) NOT NULL AUTO_INCREMENT, + name VARCHAR(30) NULL DEFAULT NULL, + age INT(11) NULL DEFAULT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS STUDENT +( + id BIGINT(20) NOT NULL AUTO_INCREMENT, + name VARCHAR(30) NULL DEFAULT NULL, + age INT(11) NULL DEFAULT NULL, + PRIMARY KEY (id) +); diff --git a/dynamic-datasource-spring-boot3-starter/src/test/resources/db/spring-expression-language.sql b/dynamic-datasource-spring-boot3-starter/src/test/resources/db/spring-expression-language.sql new file mode 100644 index 00000000..5f426ae0 --- /dev/null +++ b/dynamic-datasource-spring-boot3-starter/src/test/resources/db/spring-expression-language.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS t_user +( + id BIGINT(20) NOT NULL AUTO_INCREMENT, + name VARCHAR(30) NULL DEFAULT NULL, + age INT(11) NULL DEFAULT NULL, + PRIMARY KEY (id) +); diff --git a/pom.xml b/pom.xml index 2915ffd0..d588657a 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,7 @@ 3.9.1 1.4.2 1.18.28 + 2.2.220 3.3.1 3.1.1 @@ -165,6 +166,11 @@ lombok ${lombok.version} + + com.h2database + h2 + ${h2.version} +