spring事务默认绑定单数据源,多数据源需确保路由先于事务开启,切面@order必须小于事务切面(如-1);不支持跨数据源分布式事务,应拆分读写逻辑或统一数据源。

Spring 的事务管理默认绑定单个数据源,遇到多数据源切换时,事务不会自动跨数据源生效——这是关键前提。动态路由本身不解决事务一致性问题,必须配合事务传播机制与数据源上下文协同设计。
事务与数据源路由的生命周期必须对齐
Spring 事务在方法执行前开启,依赖当前线程绑定的 DataSource;而 AbstractRoutingDataSource 的 determineCurrentLookupKey() 是在获取 Connection 时才调用。如果 AOP 切面设置数据源标识晚于事务创建(比如放在 @Transactional 方法内部),事务就会落在默认数据源上,导致“路由了但没事务”或“有事务但没路由”。
正确顺序是:先设置数据源标识 → 再开启事务 → 执行 SQL。
因此,数据源切换切面的 @Order 必须比事务切面更高(数值更小),例如 @Order(-1)。
避免跨数据源事务操作
Spring 原生的 @Transactional 不支持跨多个独立 DataSource 的分布式事务。若一个业务方法里同时操作 master 和 slave 数据源:
- 会抛出异常或静默失败(取决于配置)
- 即使手动开启两个事务,也无法保证原子性
- 常见误写:在一个 @Transactional 方法内用 @DataSource("slave") 切换读库,又用 JdbcTemplate 直接操作主库
解决方案只有两种:
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
- 拆分逻辑:读操作走独立无事务方法(标注 @DataSource("slave")),写操作走另一事务方法(默认或 @DataSource("master"))
- 强制统一数据源:读写都走 master,放弃读写分离收益(仅调试或简单场景)
事务方法内嵌套切换需显式管理
若主事务方法调用了另一个带 @DataSource 注解的子方法,子方法的数据源切换是否生效,取决于两点:
- 子方法是否为 public,且通过 Spring 代理调用(非 this.xxx() 直接调用)
- 子方法是否声明了 @Transactional,否则其 DataSource 切面可能被忽略
建议做法:子方法不加 @Transactional,仅承担查询职责;主方法控制事务边界,并在调用前后用 DynamicDataSourceContext.setDataSourceKey() 显式切换,再调用 clearDataSourceKey() 清理。
只读事务可辅助路由判断
Spring 的 readOnly = true 属性虽不直接触发数据源切换,但可作为路由策略依据。例如:
- 在 determineCurrentLookupKey() 中检查 TransactionSynchronizationManager.isCurrentTransactionReadOnly()
- 若为 true,返回 "slave";否则返回 "master"
- 配合 @Transactional(readOnly = true) 使用,实现声明式读写分离
注意:该方式要求所有读操作都显式标注 readOnly,否则容易漏判。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










