oracle读写分离需多数据源+动态路由,应用层控制;须禁用datasourceautoconfiguration、区分配置前缀、url用service name、determinecurrentlookupkey返回精确字符串key、@primary标注routingdatasource、事务方法强制走主库。

Oracle 本身不支持 MySQL 那种原生主从复制,所谓“读写分离”在 Oracle 场景下本质是**多数据源 + 动态路由**,靠应用层控制读/写操作走哪个库。直接配 master/slave 键名会失效,必须手动定义 key 并确保 AbstractRoutingDataSource 的 determineCurrentLookupKey() 返回值与注册时的 key 完全一致。
Oracle 双数据源配置要避开 spring.datasource 自动装配
Spring Boot 默认会尝试用 spring.datasource.* 自动创建一个 DataSource,这和你后续手动注册的多个数据源冲突。必须显式关闭:
- 在
application.yml中加spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration - Oracle 连接 URL 要带 service name(不是 SID),例如:
jdbc:oracle:thin:@//192.168.1.100:1521/ORCLPDB1 - 两个数据源不能共用同一组配置前缀,比如都用
spring.datasource;推荐分开为spring.datasource.master和spring.datasource.slave
RoutingDataSource 的 determineCurrentLookupKey 必须返回字符串 key
很多人卡在这一步:Oracle 场景下,AbstractRoutingDataSource 的 determineCurrentLookupKey() 返回值必须是 String 类型,且必须和你在 setTargetDataSources() 里 put 进去的 key 完全相同(大小写敏感)。返回枚举或自定义对象会导致 No qualifying bean of type 'javax.sql.DataSource' 错误。
- 正确示例:
map.put("masterDataSource", masterDataSource)→determineCurrentLookupKey()必须返回"masterDataSource" - 不要返回
DataSourceType.MASTER枚举,除非你重写了toString()且值为"masterDataSource" - 建议统一用常量类管理 key:
public static final String MASTER = "masterDataSource";
@Primary Bean 和 @Qualifier 使用必须匹配
Spring 容器里只能有一个 @Primary 数据源,它会被 MyBatis、JPA 等框架默认注入。如果你把 @Primary 加在了 RoutingDataSource 上,但没把它设为实际的数据源 Bean(即没在 DataSourceConfig 中注册为 @Bean),事务或 SQL 执行时就会 fallback 到空数据源,报 Cannot determine embedded database driver class 或连接超时。
-
@Primary必须标注在最终对外暴露的DynamicDataSource(即继承AbstractRoutingDataSource的那个 Bean)上 - 两个真实数据源(
masterDataSource、slaveDataSource)必须用@Qualifier("xxx")显式注入到DynamicDataSource的构造或方法中 - MyBatis 的
SqlSessionFactory必须引用这个@Primary的DynamicDataSource,不能指向任一物理数据源
事务方法里强制走主库,否则写操作可能路由到从库
Oracle 没有自动读写识别能力。如果一个 Service 方法加了 @Transactional,但内部只调用纯查询方法,DynamicDataSource 仍可能按当前线程上下文(比如上一次设的 SLAVE)路由,导致 INSERT/UPDATE 失败——因为从库用户通常只有 SELECT 权限。
- 所有
@Transactional方法入口,必须显式调用DataSourceContextHolder.setDataSourceType(DataSourceType.MASTER) - 或者用 AOP 在
@Transactional注解方法执行前强制切到主库,避免遗漏 - 从库账号权限必须严格限制:仅授予
CREATE SESSION和SELECT ANY TABLE,禁止INSERT/UPDATE/DELETE
最易忽略的一点:Oracle 的 TNS 连接串格式、服务名大小写、wallet 配置(如使用 TLS)都会导致 getConnection() 报 IO Error: The Network Adapter could not establish the connection,这类错误和路由逻辑无关,但排查时容易误判为数据源切换失败。











