AbstractRoutingDataSource是切换Oracle多数据源的首选,因其在每次getConnection()前强制执行determineCurrentLookupKey()实现动态选库;需正确配置Oracle驱动类名和URL格式,推荐使用@DS注解+AOP切面管理数据源切换,并为每个数据源配置独立的PlatformTransactionManager以避免事务失效。
为什么 AbstractRoutingDataSource 是切换 Oracle 多数据源的首选
因为 spring 原生不支持运行时动态选库,而 abstractroutingdatasource 是唯一被官方文档明确设计用于此场景的扩展点——它在每次 getconnection() 调用前强制执行 determinecurrentlookupkey(),把“选哪个 oracle 实例”这件事完全交给你控制。直接继承它比手动管理 datasource 列表 + if-else 分发更安全,也避免了事务上下文污染。
Oracle 多数据源配置要注意 driver-class-name 和 URL 格式
Oracle 驱动类名必须是 oracle.jdbc.OracleDriver(旧版)或 oracle.jdbc.driver.OracleDriver(已弃用),推荐用新版 oracle.jdbc.driver.OracleDriver;URL 必须带 service name 或 SID,不能只写 IP+端口。常见错误是拼错 URL 导致 java.sql.SQLException: No suitable driver found 或连接超时。
-
jdbc:oracle:thin:@192.168.1.100:1521:ORCL(SID 方式) -
jdbc:oracle:thin:@//192.168.1.100:1521/orclpdb(Service Name 方式,推荐) - 如果用 Oracle 12c+ 的 PDB,务必用双斜杠
//开头,否则连接失败
@DS 注解 + AOP 切面是 Oracle 场景下最稳的切换方式
Oracle 连接开销大、事务敏感,用方法级注解比线程变量手动 set/remove 更可控。AOP 在方法入口设 DataSourceContextHolder.set("orcl_slave"),出口自动 clear(),能防止异步调用或异常后线程复用导致的数据源错乱。
- 注解定义要加
@Target({ElementType.METHOD, ElementType.TYPE}),支持类和方法两级生效 - AOP 切点表达式建议写成
@annotation(com.example.annotation.DS),避免匹配到 MyBatis 内部方法 - 切面里必须捕获异常并调用
DataSourceContextHolder.clear(),否则线程池中线程可能携带过期数据源
Oracle 数据源切换后事务失效的典型表现和修复
现象是:加了 @Transactional 的方法切换到从库(如 orcl_readonly)后,事务注解静默失效,INSERT/UPDATE 仍能执行但不回滚——这是因为 Spring 的 DataSourceTransactionManager 绑定的是默认数据源,不会随 AbstractRoutingDataSource 动态变化。
- 解决方案:为每个 Oracle 数据源单独配一个
PlatformTransactionManagerBean,并在@Transactional中显式指定transactionManager = "orclSlaveTx" - 或者改用
@Transactional(propagation = Propagation.NOT_SUPPORTED)强制不走事务(仅适用于纯查询) - 切忌在同一个事务方法内跨数据源操作,Oracle 不支持分布式事务(XA)时会报
ORA-02049: timeout: distributed transaction waiting for lock
真正难的不是写通切换逻辑,而是确保每次 getConnection() 拿到的都是你预期的那个 Oracle 实例——尤其在 HikariCP 连接池 + 多线程 + 异步回调混合场景下,ThreadLocal 的生命周期管理稍有疏忽,就会出现“明明设了 orcl_backup,却连到了 orcl_primary”的幽灵问题。











