Spring Boot Test中如何用通配符配置@Sql注解的脚本文件?
解决@Sql注解无法通过通配符批量执行SQL文件的问题
问题原因
使用@Sql(scripts = "/folder/*.sql")时触发以下错误:
org.springframework.jdbc.datasource.init.CannotReadScriptException:
无法从类路径资源读取SQL脚本 [folder/.sql]; 嵌套异常为java.io.FileNotFoundException: 类路径资源
[folder/.sql] 无法打开,因为它不存在
这是因为@Sql注解本身不支持通配符路径匹配,它会将*.sql视为具体文件名去查找,导致找不到对应资源。
解决方案
方案1:通过SpEL+ResourcePatternResolver动态加载脚本
借助Spring的ResourcePatternResolver扫描指定路径下的所有SQL文件,通过SpEL表达式在@SqlGroup中注入脚本路径:
import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlGroup; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Arrays; import java.util.stream.Collectors; public class BatchSqlTest { private static final ResourcePatternResolver RESOLVER = new PathMatchingResourcePatternResolver(); @Test @SqlGroup({ @Sql(scripts = "#{T(com.example.BatchSqlTest).getAllSqlScripts()}") }) void executeAllSqlScripts() { // 测试逻辑 } public static String[] getAllSqlScripts() throws IOException { Resource[] resources = RESOLVER.getResources("classpath:/folder/*.sql"); return Arrays.stream(resources) .map(resource -> { try { return resource.getURL().toString().replaceFirst("file:", ""); } catch (IOException e) { throw new RuntimeException("Failed to resolve SQL script path", e); } }) .toArray(String[]::new); } }
方案2:合并SQL脚本到单个文件
将目标文件夹下的所有SQL内容合并到一个总脚本(如all_scripts.sql),直接用常规@Sql引用:
@Sql(scripts = "/folder/all_scripts.sql") @Test void executeMergedSqlScript() { // 测试逻辑 }
该方式简单直接,适合无需单独维护单个SQL文件的场景。
方案3:自定义TestExecutionListener
实现Spring Test的TestExecutionListener,在测试方法执行前自动扫描并执行指定路径下的所有SQL文件:
import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.datasource.init.ScriptUtils; import org.springframework.test.context.TestContext; import org.springframework.test.context.support.AbstractTestExecutionListener; import javax.sql.DataSource; import java.io.IOException; public class BatchSqlListener extends AbstractTestExecutionListener { @Override public void beforeTestMethod(TestContext testContext) throws IOException { DataSource dataSource = testContext.getApplicationContext().getBean(DataSource.class); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource[] resources = resolver.getResources("classpath:/folder/*.sql"); for (Resource resource : resources) { ScriptUtils.executeSqlScript(dataSource.getConnection(), resource); } } }
在测试类上注册该监听器:
import org.springframework.test.context.TestExecutionListeners; import org.junit.jupiter.api.Test; @TestExecutionListeners(listeners = BatchSqlListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS) public class BatchSqlTest { @Test void executeAllSqlScripts() { // 测试逻辑 } }
内容的提问来源于stack exchange,提问作者mvn.2047
相关产品推荐
相关产品推荐

