背景:早期项目是springboot2.x + druid 的单数据源工程,其中使用了dblink的方式进行跨数据库访问。现在客户的机房搬迁,记账的下游数据库说是搬到不同区域,dblink的方式需要长期占用资源,需要修改成直连方式。
按照AI的指引采用:
1.在Spring Boot中配置两个不同的Oracle数据源,你需要在配置类中分别定义两个数据源,并使用AbstractRoutingDataSource来路由到不同的数据源。以下是一个配置类的示例,展示了如何设置两个不同的Oracle数据源,并在应用中使用它们。1. 添加依赖确保pom.xml或build.gradle中包含Oracle JDBC驱动依赖。
Maven:
<dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>版本</version> </dependency>
Gradle:
implementation 'com.oracle.database.jdbc:ojdbc8:版本'
2. 配置置文件在application.yml或application.properties中配置两个数据源的详细信息:
yaml:
spring: datasource: primary: driver-class-name: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@localhost:1521/orclpdb1 username: system password: manager platform: oracle secondary: driver-class-name: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@localhost:15222/orclpdb2 username: scott password: tiger platform: oracle
3. 配置置类创建配置类来定义和配置两个数据源,并设置路由逻辑:
@Configuration public class OracleDataSourceConfig { @Primary @Bean(name = "primaryDataSource") @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource(DataSourceProperties properties) { return buildDataSource(properties, "primary"); } @Bean(name = "secondaryDataSource") @ConfigurationProperties(prefix = "spring.datasource.secondary") public DataSource secondaryDataSource(DataSourceProperties properties) { return buildDataSource(properties, "secondary"); } private DataSource buildDataSource(DataSourceProperties properties, String dataSourceName) { HikariDataSource ds = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); ds.setPoolName(dataSourceName + "DataSource"); return ds; } @Bean public AbstractRoutingDataSource dataSource(@Qualifier("primaryDataSource") DataSource primary, @Qualifier("secondaryDataSource") secondary) { AbstractRoutingDataSource router = new AbstractRoutingDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("primary", primary); targetDataSources.put("secondary", secondary); router.setTargetDataSources(targetDataSources); return router; } }
4. 使用数据源在Service或Repository中,你可以通过@Qualifier指定数据源名称来使用特定的数据源:
@Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(@Qualifier("primaryDataSource") UserRepository userRepository) this.userRepository = userRepository; // your business code here }
对于动态路由到不同的数据源,你可以使用AbstractRoutingDataSource的机制,通过某种方式动态设置dataSourceLookupKey来决定使用哪个数据源。动态切换数据源示例你可以定义一个简单的策略来决定使用哪个数据源,比如基于用户ID的奇偶数或某些业务逻辑:
public class DynamicDataSourceKeyResolver implements DataSourceLookup { @Override public DataSource determineCurrentLookupKey(Method method, Object[] args) { // 假设定一个逻辑,比如根据args[0](第一个参数)来决定数据源 if ((Integer) args[0] % 2 == 0) return "primary"; return "secondary"; } } }
然后在你的服务中注入并设置这个Resolver:
@Autowired public void setDataSourceLookup(DynamicDataSourceKeyResolver resolver) { ((AbstractRoutingDataSource) dataSource).setDefaultTargetDataSourceLookup(resolver); }
这样,每次调用到数据源的地方,都会根据你的逻辑动态切换到不同的数据源。请注意,实际应用中策略需根据具体业务需求调整。
这些配置还是太复杂了,看下引入dynamic-datasource-spring-boot-starter
dynamic-datasource-spring-boot-starter 是一个简化Spring Boot应用中配置多数据源管理的开源库。它支持动态切换数据源,可以方便地处理读写分离、分库等场景。下面是引入并使用 dynamic-datasource-spring-boot-starter 的简化配置步骤:
1. 添加依赖首先,在你的Spring Boot项目的pom.xml或build.gradle文件中加入 dynamic-datasource-spring-boot-starter 的依赖。
Maven:
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter <version>最新版本号</version> </dependency>
2. YAML配置在application.yml文件中配置你的数据源,包括主数据源和其他数据源:
spring: datasource: dynamic: primary: master strict: false # 是否严格匹配数据源名称,默认false datasource: master: driver-class-name: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@localhost:1521/orclpdb1 username: system password: manager slave: driver-class-name: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@localhost:15222/orclpdb2 username: scott password: tiger
3. 使用多数据源在业务代码中,你可以通过@DS注解器来指定使用特定的数据源,或者不指定时使用默认的数据源。
3.1. 在Service或Repository中指定数据源
@Service public class UserService { @DS("slave") private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } // 业务逻辑... }
3.2. 动态切换数据源在需要动态切换数据源的场景下,可以通过DynamicDataSourceContextHolder来实现。
public class SomeService { public void someMethodBasedOnCondition() { if (需要使用主数据源) { DataSourceContextHolder.setDataSourceType("master"); } else { DataSourceContextHolder.setDataSourceType("slave"); } // 执行数据库操作... DataSourceContextHolder.clearDataSourceType(); // 清除上下文,恢复默认数据源 } } }
4. 自动配置切换策略dynamic-datasource-spring-boot-starter也支持自动路由策略,比如读写分离。
你可以配置master-slave来自动路由读写操作到主库,读操作到从库。
spring: datasource: dynamic: strategy: master-slave
之前的单数据源可以通过配置druid的属性:
connectionProperties: config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIRTeF9hVfHv0qiOpzgQYW0/oQk6sQp278uwtBONYj2cKqF2OcBD8...==
指定数据库敏感信息不对称加解密的公钥
但是换成多数据源后,并不会触发解密密码字段。
AI后面给出的两三个办法都是行不通的。。。。
当然,如果你的应用需要配置多个Druid数据源,并且每个数据源的密码都需要通过自定义的解密方法解密,你可以扩展上面的配置方法以适应多数据源的场景。以下是实现多个Druid数据源并共用同一解密方法的一个示例:自定义解密工具类(与单数据源时相同)
package com.example.utils; import org.springframework.stereotype.Component; @Component public class EncryptionUtils { public String decrypt(String encryptedText) { // 实际解密逻辑 return encryptedText; // 示例返回,实际应替换为解密后明文 } }
package com.example.config; import com.alibaba.druid.pool.DruidDataSource; import com.example.utils.EncryptionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConstructorBinding; import java.util.HashMap; import java.util.Map; @Configuration public class MultiDataSourceConfig { @Autowired private EncryptionUtils encryptionUtils; // 假设有两个数据源的配置类,你可以根据实际需要增加更多 @ConstructorBinding @ConfigurationProperties(prefix = "spring.datasource.db1") public static class Db1DataSourceProperties { private String url; private String username; private String passwordEncrypted; // 加密的密码 // getter & setter } @ConstructorBinding @ConfigurationProperties(prefix = "spring.datasource.db2") public static class Db2DataSourceProperties { private String url; private String username; private String passwordEncrypted; // 加密的密码 // getter & setter } @Bean(name = "db1DataSource") public DataSource db1DataSource(Db1DataSourceProperties properties) { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(properties.getUrl()); dataSource.setUsername(properties.getUsername()); dataSource.setPassword(encryptionUtils.decrypt(properties.getPasswordEncrypted())); // 其他配置 return dataSource; } @Bean(name = "db2DataSource") public DataSource db2DataSource(Db2DataSourceProperties properties) { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(properties.getUrl()); dataSource.setUsername(properties.getUsername()); dataSource.setPassword(encryptionUtils.decrypt(properties.getPasswordEncrypted())); // 其他配置 return dataSource; } // 如果需要动态数据源路由,可参考dynamic-datasource-spring-boot-starter或自定义AbstractRoutingDataSource逻辑 }
按照网上的passwordCallback也是不行
最后是下面的方法搞定了:
Spring: datasource: dynamic: primary: master public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI....== datasource: master: url: jdbc:oracle:thin:@127.0.0.1:1521/abcd password: ENC(加密后密文2) username: ENC(加密后密文1) slave: url: jdbc:oracle:thin:@127.0.0.1/defg password: 不加密原文