按照上述方法配置后报错,在调用measuredPowerList方法时候,stationInfoMa...

Created on: January 28, 2026

Answered using GPT-5.2 Thinking by Chat01

Question

DataSourceConfig.java
DataSourceFieldProxyFactory.java
DynamicDataSource.java
FieldBoundDataSourceProxy.java
TargetDataSource.java
TargetDataSourceAspect.java
TargetDataSourceFieldProxyPostProcessor.java

按照上述方法配置后报错,在调用measuredPowerList方法时候,stationInfoMapper没有使用指定的数据源,即便字段有注解,mapper的类上有注解,显示:切换数据源 -> ypm-sync,但查询时候没有查到数据报错了(这里try所以没有打印没找表的错误信息),整个调用链没有默认使用事务,或者@Transactional注解

log
[DEBUG] [2026-01-28 18:12:57] [TargetDataSourceAspect.java-37] 未匹配数据源注解,当前数据源栈顶:null,使用默认数据源 [DEBUG] [2026-01-28 18:12:57] [BaseJdbcLogger.java-135] ==> Preparing: SELECT station_id,out_sys_id,name,abbr,district_id,region_id,partition_id,category,station_type,planning_capacity,margin,grid_status,grid_date,company_id,method_type,predict_factory,access_ugc,longitude,latitude,remark,creator,updater,create_time,update_time,delete_flag FROM power_station WHERE (station_id = ? AND delete_flag = ?) [DEBUG] [2026-01-28 18:12:57] [BaseJdbcLogger.java-135] ==> Parameters: 200000091(Long), false(Boolean) [DEBUG] [2026-01-28 18:12:57] [BaseJdbcLogger.java-135] <== Total: 1 [DEBUG] [2026-01-28 18:12:57] [TargetDataSourceAspect.java-34] 切换数据源 -> ypm-sync [DEBUG] [2026-01-28 18:12:57] [BaseJdbcLogger.java-135] ==> Preparing: SELECT id,name,abbr,type,district,capacity_plan,capacity_grid,latitude,longitude,access_time,update_time FROM station_info WHERE (abbr = ?) [DEBUG] [2026-01-28 18:12:57] [BaseJdbcLogger.java-135] ==> Parameters: ZDJSGF(String) [DEBUG] [2026-01-28 18:12:57] [TargetDataSourceAspect.java-45] 恢复数据源,当前栈顶 = null
java
package com.wiscom.sync.dao; import com.wiscom.basic.enums.DataEnum; import com.wiscom.station.mapper.PowerStationMapper; import com.wiscom.station.model.entity.PowerStation; import com.wiscom.sync.mapper.MeasuredPowerStationMapper; import com.wiscom.sync.mapper.PowerDataMapper; import com.wiscom.sync.mapper.StationInfoMapper; import com.wiscom.sync.model.entity.MeasuredPowerStation; import com.wiscom.sync.model.entity.PowerData; import com.wiscom.sync.model.entity.StationInfo; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @Slf4j @Repository public class PowerDataDao { @Resource private PowerStationMapper stationMapper; @Resource @TargetDataSource(name = "ypm-sync") private StationInfoMapper stationInfoMapper; @Resource private MeasuredPowerStationMapper measuredPowerStationMapper; @Resource private PowerDataMapper powerDataMapper; /** * 查询可视化文件上报实际功率(场站id映射ypm-sync同步id) * * @param stationId 场站id(弃电场站id) * @param startTime 开始时间 * @param endTime 结束时间 * @return ypm文件上报功率 */ public List<MeasuredPowerStation> measuredPowerList(Long stationId, LocalDateTime startTime, LocalDateTime endTime) { PowerStation powerStation = stationMapper.stationDetail(stationId); StationInfo stationInfo = stationInfoMapper.stationByAbbr(powerStation.getAbbr()); if (stationInfo == null) { return Collections.emptyList(); } return measuredPowerStationMapper.measuredPowerList(Long.valueOf(stationInfo.getId()), startTime, endTime) .stream().peek(o -> o.setStationId(stationId)).collect(Collectors.toList()); } }
java
package com.wiscom.sync.mapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.wiscom.basic.config.datasource.TargetDataSource; import com.wiscom.sync.model.entity.StationInfo; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper @TargetDataSource(name = "ypm-sync") public interface StationInfoMapper extends BaseMapper<StationInfo> { /** * 获取所有站点信息(可视化台账) * * @return 站点信息列表 */ default List<StationInfo> listAll() { return selectList(new LambdaQueryWrapper<>()); } /** * 根据缩写查询场站信息 * * @param abbr 缩写 * @return 场站信息 */ default StationInfo stationByAbbr(String abbr) { return selectOne(new LambdaUpdateWrapper<StationInfo>().eq(StationInfo::getAbbr, abbr)); } }

Thought

Explaining fallback behavior and solution

The log shows that measuredPowerList wasn’t annotated, and the DataSource stack was empty, so the default was used for the stationMapper query. Later, the log shows 切换数据源 -> ypm-sync, indicating the aspect matched @TargetDataSource. If the ypm-sync DataSource isn’t registered, fallback happens (likely due to lenientFallback). To resolve this, check if the ypm-sync DataSource is properly configured in DataSourceConfig.

Verifying configuration and troubleshooting

Check if the ypmSync property exists in the current profile. If it's null, ypm-sync won't be in the data source map. Add a startup check or set lenientFallback to false in DynamicDataSource. Transaction involvement may prevent DS switching, so verify using transaction management functions. Additionally, confirm MyBatis uses the correct DynamicDataSource. Ensure the field proxy's BPP runs after injection by checking the postProcessor's order.

Handling annotation and method behavior

If the value is null, it returns early, meaning the field won't be wrapped before @Resource injection. Setting @Order helps with the execution order but isn’t strictly necessary. The log suggests the aspect matches method-level/class-level annotation, so no need for a field annotation. For explicit DS switching, consider marking measuredPowerList with @TargetDataSource("ypm-sync"). However, using this for the entire method may not be correct if the first query needs to stay on the default DS.

Analyzing DataSource and method behavior

I’m considering adding @TargetDataSource(name="ypm-sync") to the stationByAbbr method. The interface is already annotated, but AOP might not intercept default methods properly. The SQL should run inside the same method, so the DataSource switch should be fine. If there’s no "Total" log, it’s likely due to an exception, possibly "relation station_info does not exist". This could be related to schema issues in Postgres.

Investigating DataSource configuration issues

If the table is in a different schema in the ypm-sync DB, it might be missing. You could confirm by querying select current_database(), current_schema() in the mapper method. Also, there's a bug related to elss_centrally, where the bean prefix mismatches the key. Additionally, we should ensure proper DataSource injection (with @Qualifier and @Resource) and log the actual DataSource resolution.

Thought for 5m 35s

Answer

从你这段日志看,AOP 确实“识别到了”@TargetDataSource(name="ypm-sync") 并且 push 了 key,因为已经打印了 切换数据源 -> ypm-sync(来自 TargetDataSourceAspectDynamicDataSource.push(ds) 那段逻辑)。(TargetDataSourceAspect.java)
但紧接着执行 station_info 的 SQL 后没有出现 <== Total: ...,直接就 finally恢复数据源 了——这非常像 SQL 执行阶段抛异常(你说 try 吃掉了,所以没看到“找不到表”)

结合你当前 DynamicDataSource 的实现,最常见的真实原因是:

1) “切换日志有了”,但路由实际可能还是走了默认库(静默回退)

你这个 DynamicDataSource 继承的是 AbstractRoutingDataSource,只实现了 determineCurrentLookupKey() 返回 peek(),并没有做“key 不存在就报错”的处理。(DynamicDataSource.java)
AbstractRoutingDataSource 默认是 lenient fallback(key 找不到就用 defaultTargetDataSource)——所以会出现一种很迷惑的现象:

  • 业务日志:显示 “切换数据源 -> ypm-sync”
  • 但如果 ypm-sync 根本没注册进路由 map,最终还是会用默认库去跑 station_info,然后就可能“找不到表”。

为什么 ypm-sync 可能没注册进 map?

看你的配置:ypm-sync 这个 DataSource bean 是 @ConditionalOnProperty(prefix="spring.datasource.ypm-sync", name="url") 的。(DataSourceConfig.java)
并且在组装路由 map 时,只有 ypmSync != null 才会 put("ypm-sync", ypmSync)。(DataSourceConfig.java)

所以只要你当前启动 profile 下 spring.datasource.ypm-sync.url 没生效 / 写错 / 没加载到,就会导致:

  • ypmSync 为 null
  • 路由表里没有 "ypm-sync"
  • 切换 key 仍然打印,但最终查询静默回退默认库

2) 立刻把“静默回退”改成“直接报错”,你就能一眼定位

建议你直接把 DynamicDataSource 变“严格模式”,让 key 不存在时立刻抛错(而不是悄悄用默认库):

java
public class DynamicDataSource extends AbstractRoutingDataSource { public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); // 关键:关闭 lenient fallback(避免 key 不存在时悄悄走默认库) super.setLenientFallback(false); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return peek(); } // ... push/peek/poll 保持不变 }

这样只要 "ypm-sync" 没进 map,你会直接得到类似:

Cannot determine target DataSource for lookup key [ypm-sync]

定位效率会高很多。


3) 同步检查两件事(90% 的坑都在这)

  1. 启动时确认 ypm-sync Bean 是否创建成功
    因为它受 @ConditionalOnProperty(prefix="spring.datasource.ypm-sync", name="url") 控制。(DataSourceConfig.java)

  2. 确认路由 map 里确实 put 进了 "ypm-sync"
    你这里是 if (ypmSync != null) dsMap.put("ypm-sync", ypmSync);(DataSourceConfig.java)
    ——可以在 dataSource() 方法里加一行日志打印 dsMap.keySet(),一看就知道有没有。


4) 事务场景的额外提醒(虽然你说没用事务,但值得排雷)

如果外层其实存在事务(比如某个 service 方法上有 @Transactional,或者用了 TransactionTemplate),第一条 SQL 拿到的 Connection 会绑定在线程上,后面你再 push("ypm-sync") 也可能不生效(继续用同一个连接)。
你可以临时在切换前后打印 TransactionSynchronizationManager.isActualTransactionActive() 来确认是否真的“全链路无事务”。


5) 你做的“字段代理”机制本身没问题,但也解释一下你日志为什么仍然打印 AOP 切换

你加的 TargetDataSourceFieldProxyPostProcessor 会把标了 @TargetDataSource 的接口字段替换成一个 JDK Proxy,在调用前后 push/poll。(TargetDataSourceFieldProxyPostProcessor.java) (DataSourceFieldProxyFactory.java)
TargetDataSourceAspect 只有在 joinPoint.getTarget()FieldBoundDataSourceProxy 时才跳过。(TargetDataSourceAspect.java)
你现在日志里依然出现 切换数据源 -> ypm-sync,说明实际被 AOP 拦截执行的那个对象并不是字段代理本身(这不一定是错,只是说明你现在主要靠 AOP/接口注解在切换)。


最短结论

你现在的现象非常符合:“ypm-sync 没有真的注册到 DynamicDataSource 的 targetDataSources 里,导致路由静默回退默认库”
按上面第 2 点把 setLenientFallback(false) 加上 + 启动时打印 dsMap.keySet(),基本一轮就能把问题钉死。

Share this Q&A