按照上述方法配置后报错,在调用measuredPowerList方法时候,stationInfoMa...
생성일: 2026년 1월 28일
답변에 사용된 모델 GPT-5.2 Thinking by Chat01
생성일: 2026년 1월 28일
답변에 사용된 모델 GPT-5.2 Thinking by Chat01
按照上述方法配置后报错,在调用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
javapackage 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()); } }
javapackage 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)); } }
从你这段日志看,AOP 确实“识别到了”@TargetDataSource(name="ypm-sync") 并且 push 了 key,因为已经打印了 切换数据源 -> ypm-sync(来自 TargetDataSourceAspect 里 DynamicDataSource.push(ds) 那段逻辑)。(TargetDataSourceAspect.java)
但紧接着执行 station_info 的 SQL 后没有出现 <== Total: ...,直接就 finally 里 恢复数据源 了——这非常像 SQL 执行阶段抛异常(你说 try 吃掉了,所以没看到“找不到表”)。
结合你当前 DynamicDataSource 的实现,最常见的真实原因是:
你这个 DynamicDataSource 继承的是 AbstractRoutingDataSource,只实现了 determineCurrentLookupKey() 返回 peek(),并没有做“key 不存在就报错”的处理。(DynamicDataSource.java)
而 AbstractRoutingDataSource 默认是 lenient fallback(key 找不到就用 defaultTargetDataSource)——所以会出现一种很迷惑的现象:
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"建议你直接把 DynamicDataSource 变“严格模式”,让 key 不存在时立刻抛错(而不是悄悄用默认库):
javapublic 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]
定位效率会高很多。
启动时确认 ypm-sync Bean 是否创建成功
因为它受 @ConditionalOnProperty(prefix="spring.datasource.ypm-sync", name="url") 控制。(DataSourceConfig.java)
确认路由 map 里确实 put 进了 "ypm-sync"
你这里是 if (ypmSync != null) dsMap.put("ypm-sync", ypmSync);(DataSourceConfig.java)
——可以在 dataSource() 方法里加一行日志打印 dsMap.keySet(),一看就知道有没有。
如果外层其实存在事务(比如某个 service 方法上有 @Transactional,或者用了 TransactionTemplate),第一条 SQL 拿到的 Connection 会绑定在线程上,后面你再 push("ypm-sync") 也可能不生效(继续用同一个连接)。
你可以临时在切换前后打印 TransactionSynchronizationManager.isActualTransactionActive() 来确认是否真的“全链路无事务”。
你加的 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(),基本一轮就能把问题钉死。