升级到Java17+Spring Boot2.5.5后单元测试@Sql执行脚本异常如何解决?
解决方案
根因确认
你定位的问题完全正确:Spring Boot 2.5.x版本升级后,默认会对Hikari数据源生成AOP代理对象,而JpaTransactionManager和@Sql注解逻辑拿到的是两个不同的代理实例,代理类未正确重写equals方法导致比较失败,触发校验异常。
可落地修复方案
- 方案1:修改@Sql注解配置(最快修复,无侵入)
给@Sql注解显式指定独立事务模式,跳过数据源和事务管理器的绑定校验:
该模式下@Sql会单独申请数据库连接执行脚本,不依赖测试方法的事务上下文,不会触发数据源一致性校验。@Sql(scripts = "/clear_data.sql", executionPhase = ExecutionPhase.AFTER_TEST_METHOD, transactionMode = Sql.TransactionMode.ISOLATED) - 方案2:关闭测试环境数据源代理
在测试环境的application-test.properties配置文件中添加配置,关闭数据源自动代理:
关闭代理后事务管理器和@Sql逻辑拿到的都是原始Hikari数据源实例,equals比较正常。spring.datasource.hikari.allow-pool-suspension=false # 若全局开启了AOP自动代理可追加以下配置,仅测试环境生效 spring.aop.auto=false - 方案3:升级Spring Boot版本(官方修复)
该问题属于Spring Framework 5.3.x早期版本的已知bug,官方在后续版本优化了代理对象的比较逻辑,你可以选择将Spring Boot版本升级到2.5.15及以上,或者2.6.x/2.7.x稳定版本,升级后无需修改业务代码即可解决问题。 - 方案4:自定义测试监听器(特殊场景使用)
如果前三种方案都不符合你的业务要求,可以重写Sql执行的校验逻辑,解代理后再比较数据源:
然后在测试类上替换默认的Sql执行监听器即可:public class CustomSqlScriptsTestExecutionListener extends SqlScriptsTestExecutionListener { @Override protected boolean sameDataSource(DataSource ds1, DataSource ds2) { if (ds1 == ds2) { return true; } // 解开代理获取真实数据源对象 Object realDs1 = AopProxyUtils.getSingletonTarget(ds1); Object realDs2 = AopProxyUtils.getSingletonTarget(ds2); realDs1 = realDs1 == null ? ds1 : realDs1; realDs2 = realDs2 == null ? ds2 : realDs2; return realDs1.equals(realDs2); } }@TestExecutionListeners( value = CustomSqlScriptsTestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS )
内容的提问来源于stack exchange,提问作者KnockKnock
相关产品推荐
相关产品推荐

