• 基于Spring boot轻松实现一个多数据源框架


    Spring Boot 提供了 Data JPA 的包,允许你使用类似 ORM 的接口连接到 RDMS。它很容易使用和实现,只需要在 pom.xml 中添加一个条目(如果使用的是 Maven,Gradle 则是在 build.gradle 文件中)。

    1. <dependencies>
    2. <!-- Spring boot 依赖 -->
    3. <dependency>
    4. <groupId>org.springframework.boot</groupId>
    5. <artifactId>spring-boot-starter</artifactId>
    6. </dependency>
    7. <dependency>
    8. <groupId>org.springframework.boot</groupId>
    9. <artifactId>spring-boot-starter-data-jpa</artifactId>
    10. </dependency>
    11. </dependencies>

    在Main Spring Application类中添加 2 个注释:

    1. @SpringBootApplication
    2. @EnableJpaRepositories
    3. @EnableAutoConfiguration
    4. public class SpringMainApplication {
    5. public static void main(String[] args) {
    6. SpringApplication.run(SpringMainApplication.class, args);
    7. }
    8. }

    最后添加一个数据库连接包,配置数据库连接即可实现与数据库通信。

    接下来,我们开始配置多数据源连接。

    注意:多个数据库应该具有相同的驱动程序。无法连接到不同的数据库,如 MySql 和 Postgres SQL 数据库。数据库必须相同。此外,数据库模式必须相同,不同模式的 2 个数据库无法进行连接。

    多数据源有哪些应用场景?

    1.支持具有相同模式的同一应用程序内的多租户。

    2.动态模拟多个环境数据库上的行为 ,而不需要重新启动应用程序。 例如,你可以动态连接到开发数据库或 QA 数据库,而无需重新启动应用程序。

    3.支持多个数据库来模拟各种自动化测试场景。不同数据库可能具有不同的配置和静态信息,意味着你可以用一个自动化测试脚本覆盖多个测试用例。

    4.在同一个应用程序中支持多个组织。根据用户登录,可以动态决定他们的数据应进入哪个组织的数据库。

    5.一次性为多个数据库插入数据。例如,你有一个从脚本创建数据的批处理作业,你可以一次性连接到多个数据库,并对所有这些数据库运行脚本,而无需指向不同的应用程序或重新启动服务器来执行此操作。

    多数据源示意图如下:

    第一步:添加 pom 依赖

    1. <dependencies>
    2. <!-- Spring boot dependencies -->
    3. <dependency>
    4. <groupId>org.springframework.boot</groupId>
    5. <artifactId>spring-boot-starter</artifactId>
    6. </dependency>
    7. <dependency>
    8. <groupId>org.springframework.boot</groupId>
    9. <artifactId>spring-boot-starter-actuator</artifactId>
    10. </dependency>
    11. <dependency>
    12. <groupId>org.springframework.boot</groupId>
    13. <artifactId>spring-boot-devtools</artifactId>
    14. </dependency>
    15. <dependency>
    16. <groupId>org.springframework.boot</groupId>
    17. <artifactId>spring-boot-starter-data-jpa</artifactId>
    18. </dependency>
    19. <dependency>
    20. <groupId>org.springframework.boot</groupId>
    21. <artifactId>spring-boot-starter-web</artifactId>
    22. </dependency>
    23. <!-- Swagger dependencies -->
    24. <dependency>
    25. <groupId>io.springfox</groupId>
    26. <artifactId>springfox-swagger2</artifactId>
    27. <version>2.9.2</version>
    28. </dependency>
    29. <dependency>
    30. <groupId>io.springfox</groupId>
    31. <artifactId>springfox-swagger-ui</artifactId>
    32. <version>2.9.2</version>
    33. </dependency>
    34. <!-- lombok dependency -->
    35. <dependency>
    36. <groupId>org.projectlombok</groupId>
    37. <artifactId>lombok</artifactId>
    38. <version>1.18.12</version>
    39. <scope>provided</scope>
    40. </dependency>
    41. <!-- Database dependency -->
    42. <dependency>
    43. <groupId>org.postgresql</groupId>
    44. <artifactId>postgresql</artifactId>
    45. </dependency>
    46. <!-- test dependencies -->
    47. <dependency>
    48. <groupId>org.springframework.boot</groupId>
    49. <artifactId>spring-boot-starter-test</artifactId>
    50. <scope>test</scope>
    51. <exclusions>
    52. <exclusion>
    53. <groupId>org.junit.vintage</groupId>
    54. <artifactId>junit-vintage-engine</artifactId>
    55. </exclusion>
    56. </exclusions>
    57. </dependency>
    58. </dependencies>
    59. <dependencyManagement>
    60. <dependencies>
    61. <dependency>
    62. <groupId>org.springframework.cloud</groupId>
    63. <artifactId>spring-cloud-starter-parent</artifactId>
    64. <version>${spring-cloud-dependencies.version}</version>
    65. <type>pom</type>
    66. <scope>import</scope>
    67. </dependency>
    68. <dependency>
    69. <groupId>org.springframework.cloud</groupId>
    70. <artifactId>spring-cloud-gcp-dependencies</artifactId>
    71. <version>${project.version}</version>
    72. <type>pom</type>
    73. <scope>import</scope>
    74. </dependency>
    75. </dependencies>
    76. </dependencyManagement>
    77. <build>
    78. <plugins>
    79. <plugin>
    80. <groupId>org.springframework.boot</groupId>
    81. <artifactId>spring-boot-maven-plugin</artifactId>
    82. </plugin>
    83. <plugin>
    84. <groupId>org.apache.maven.plugins</groupId>
    85. <artifactId>maven-surefire-plugin</artifactId>
    86. </plugin>
    87. </plugins>
    88. </build>

    第二步:添加数据库连接配置

    1. app.datasource.db1.jdbc-url=jdbc:postgresql://db1.com:5432/dbname1
    2. app.datasource.db1.username=postgres
    3. app.datasource.db1.password=password
    4. app.datasource.db2.jdbc-url=jdbc:postgresql://db2.com:5432/dbname2
    5. app.datasource.db2.username=postgres
    6. app.datasource.db2.password=password
    7. app.datasource.db3.jdbc-url=jdbc:postgresql://db3.com:5432/dbname3
    8. app.datasource.db3.username=postgres
    9. app.datasource.db3.password=password

    这是 3 个独立的 PostgresSQL 实例,具有相同的模式但具有不同的数据。

    第三步:添加多数据库配置。

    首先,在 Spring 应用程序主文件中添加注解:

    1. @SpringBootApplication
    2. @EnableJpaRepositories
    3. @EnableAutoConfiguration
    4. public class MultidatabaseApplication {
    5. public static void main(String[] args) {
    6. SpringApplication.run(MultidatabaseApplication.class, args);
    7. }
    8. }

    添加配置类:

    1. @Configuration
    2. @EnableTransactionManagement
    3. @EnableJpaRepositories(
    4. entityManagerFactoryRef = "multiEntityManager",
    5. transactionManagerRef = "multiTransactionManager")
    6. @EntityScan("com.sample.client.repositories.dto.entity")
    7. public class DatabaseConfiguration {
    8. //添加 JPA 实体路径
    9. private final String PACKAGE_SCAN = "com.sample.client.repositories.dto.entity";
    10. // 将db1设置为主数据库
    11. @Primary
    12. @Bean(name = "db1DataSource")
    13. @ConfigurationProperties("app.datasource.db1")
    14. public DataSource db1DataSource() {
    15. return DataSourceBuilder.create().type(HikariDataSource.class).build();
    16. }
    17. //db2连接数据源注入
    18. @Bean(name = "db2DataSource")
    19. @ConfigurationProperties("app.datasource.db2")
    20. public DataSource db2DataSource() {
    21. return DataSourceBuilder.create().type(HikariDataSource.class).build();
    22. }
    23. //db3连接数据源注入
    24. @Bean(name = "db3DataSource")
    25. @ConfigurationProperties("app.datasource.db3")
    26. public DataSource db3DataSource() {
    27. return DataSourceBuilder.create().type(HikariDataSource.class).build();
    28. }
    29. //多数据源配置
    30. @Bean(name = "multiRoutingDataSource")
    31. public DataSource multiRoutingDataSource() {
    32. Map<Object, Object> targetDataSources = new HashMap<>();
    33. targetDataSources.put(ClientNames.DB1, db1DataSource());
    34. targetDataSources.put(ClientNames.DB2, db2DataSource());
    35. targetDataSources.put(ClientNames.DB3, db3DataSource());
    36. MultiRoutingDataSource multiRoutingDataSource
    37. = new MultiRoutingDataSource();
    38. multiRoutingDataSource.setDefaultTargetDataSource(db1DataSource());
    39. multiRoutingDataSource.setTargetDataSources(targetDataSources);
    40. return multiRoutingDataSource;
    41. }
    42. //多实体配置代码
    43. @Bean(name = "multiEntityManager")
    44. public LocalContainerEntityManagerFactoryBean multiEntityManager() {
    45. LocalContainerEntityManagerFactoryBean em
    46. = new LocalContainerEntityManagerFactoryBean();
    47. em.setDataSource(multiRoutingDataSource());
    48. em.setPackagesToScan(PACKAGE_SCAN);
    49. HibernateJpaVendorAdapter vendorAdapter
    50. = new HibernateJpaVendorAdapter();
    51. em.setJpaVendorAdapter(vendorAdapter);
    52. em.setJpaProperties(hibernateProperties());
    53. return em;
    54. }
    55. @Bean(name = "multiTransactionManager")
    56. public PlatformTransactionManager multiTransactionManager() {
    57. JpaTransactionManager transactionManager
    58. = new JpaTransactionManager();
    59. transactionManager.setEntityManagerFactory(
    60. multiEntityManager().getObject());
    61. return transactionManager;
    62. }
    63. @Primary
    64. @Bean(name="entityManagerFactory")
    65. public LocalSessionFactoryBean dbSessionFactory() {
    66. LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
    67. sessionFactoryBean.setDataSource(multiRoutingDataSource());
    68. sessionFactoryBean.setPackagesToScan(PACKAGE_SCAN);
    69. sessionFactoryBean.setHibernateProperties(hibernateProperties());
    70. return sessionFactoryBean;
    71. }
    72. //添加 hibernate 属性
    73. private Properties hibernateProperties() {
    74. Properties properties = new Properties();
    75. properties.put("hibernate.show_sql", true);
    76. properties.put("hibernate.format_sql", true);
    77. properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
    78. properties.put("hibernate.id.new_generator_mappings", false);
    79. properties.put("hibernate.jdbc.lob.non_contextual_creation", true);
    80. return properties;
    81. }
    82. }

    这样就完成了我们的多数据库配置。

    com.sample.client.repositories.dto.entity — 此目录包含 3 个数据库通用的 JPA 实体。

    MultiRoutingDataSource类是我们的实际实现,允许我们连接到多个数据库

    接下来,我们还需要一个DBContextHolder类来保存数据库引用并在运行时动态更改数据库。

    1. public class DBContextHolder {
    2. private static final ThreadLocal contextHolder = new ThreadLocal<>();
    3. public static void setCurrentDb(ClientNames dbType) {
    4. contextHolder.set(dbType);
    5. }
    6. public static ClientNames getCurrentDb() {
    7. return contextHolder.get();
    8. }
    9. public static void clear() {
    10. contextHolder.remove();
    11. }
    12. }

    ClientNames枚举类如下:

    1. public enum ClientNames {
    2. DB1, DB2, DB3
    3. }

    接下来我们需要对MultiRoutingDataSource进行重写

    1. public class MultiRoutingDataSource extends AbstractRoutingDataSource {
    2. @Override
    3. protected Object determineCurrentLookupKey() {
    4. return DBContextHolder.getCurrentDb();
    5. }
    6. }

    determineCurrentLookupKey 方法用于决定应用程序应该动态连接到哪个数据库。

    好了,我们的配置就完成了。接下来,我们测试下多数据源是否生效:

    1. @RestController
    2. @RequestMapping("/client")
    3. public class ClientDataController {
    4. @Autowired
    5. private ClientMasterService clientMasterService;
    6. @GetMapping("/{clientdb}")
    7. public String findFromDatabase(@PathVariable String clientdbName) {
    8. return clientMasterService.getClientNames(clientdbName);
    9. }
    10. }

    ClientMasterService实现如下:

    1. @Service
    2. public class ClientMasterService {
    3. @Autowired
    4. private ClientMasterRepository clientMasterRepository;
    5. public String getClientNames(String client) {
    6. switch (client) {
    7. case "db1":
    8. DBContextHolder.setCurrentDb(ClientNames.DB1);
    9. break;
    10. case "db2":
    11. DBContextHolder.setCurrentDb(ClientNames.DB2);
    12. break;
    13. case "db3":
    14. DBContextHolder.setCurrentDb(ClientNames.DB3);
    15. break;
    16. }
    17. Entity1 e1 = clientMasterRepository.findByEntity1Name("John Doe");
    18. if(e1 != null) {
    19. return "found in database: " + client + " with id " + e1.getId();
    20. }
    21. return "found in " + client + " nada!";
    22. }
    23. }

    ClientMasterService使用DBContextHolder类根据从 Rest 端点传入的数据库名称(db1、db2 或 db3)设置要指向的数据库。

    最后,编写 JPA Repository 基础代码:

    1. @Repository
    2. public interface ClientMasterRepository extends JpaRepository<Entity1, String> {
    3. Entity1 findByEntity1Name(String name);
    4. }

    Entity1 类如下:

    1. @Entity
    2. @Table(name = "entity1")
    3. @Getter
    4. @Setter
    5. public class Entity1 implements Serializable {
    6. @Id
    7. @Column(name = "id", nullable = false)
    8. private Integer id;
    9. @Column(name = "entity1Name")
    10. private String entity1Name;
    11. }

    这样就完成了整个多数据源的配置!!!

    总结

    如果你有多租户需求,或者多环境测试需求等,可以自己尝试编写一个多数据源框架,也可以引入第三方库来解决此需求。

    介绍一款Java+Springboot+Vue的前后端分离项目

    这是一款基于SpringBoot+Vue的前后端分离的项目,麻雀虽小,五脏俱全,开箱即用!

    JNPF开发平台的前端采用Vue.js,这是一种流行的前端JavaScript框架,用于构建用户界面。Vue.js具有轻量级、可扩展性强和生态系统丰富等特点,被广泛应用于构建单页面应用程序。

    后端采用SpringBoot,这是一种基于Java的开源框架,用于简化Spring应用的初始搭建以及开发过程。SpringBoot通过自动配置和约定大于配置的原则,简化了Spring应用的配置和开发。此外,JNPF还采用MyBatis-Plus作为持久层框架,它是一个功能强大的MyBatis扩展,可以大大简化数据库操作的开发。

    核心功能:表单引擎、可视化引擎、BI引擎、流程引擎、权限引擎、门户引擎、大屏引擎、接口中心、物联平台

    尝试自己开发一个应用!应用地址:https://www.jnpfsoft.com/?csdn

  • 相关阅读:
    【SpringCloudAlibaba】Seata分布式事务使用
    Jetson nano 系统安装
    关键字查询方法
    关键词搜索1688商品数据接口(标题|主图|SKU|价格|优惠价|掌柜昵称|店铺链接|店铺所在地)
    深入理解Java单例模式和优化多线程任务处理
    tortoiseSVN树冲突解决方案
    Panda3d 动画模型
    使用阿里云OSS实现头像上传
    浏览器安全-同源策略和CORS
    执行ls /dev/pts为什么这么慢?
  • 原文地址:https://blog.csdn.net/wangonik_l/article/details/133911497