Spring Boot应用中SQL脚本能否引用环境变量?如何实现?
问题
在使用JUnit的@Sql注解执行SQL测试脚本时,脚本里的用户名、密码等数据是硬编码的,想实现像Java代码中引用属性文件键值对那样的动态替换,询问是否可行及具体实现方法。
可行实现方法
方法一:Spring属性占位符替换(推荐,通用性强)
这是最常用的方案,利用Spring的属性解析能力替换SQL脚本中的占位符:
- 定义属性文件
在src/test/resources下创建属性文件(比如test-accounts.properties),写入需要动态替换的变量:
test.account.mickey.username=mickey_m test.account.mickey.password=$2y$10$ZdqfOo1vwdHJJGnkmGrw/OUelZcU9ZfRFaX/RMN3XniXH96eTkB1e test.account.minerva.username=minerva_m test.account.minerva.password=$2y$10$PiZxGGi904rCLdTSGY1ycuQhcEtQrP1u74KvQ2IEuk5Jh18Ml.6xO
- 配置测试类
在测试类上添加@TestPropertySource加载属性文件,同时在@Sql注解中配置SqlConfig开启占位符解析:
@Sql( executionPhase = BEFORE_TEST_METHOD, value = BASE_SCRIPT_PATH + "CreateCommentTest/before.sql", config = @SqlConfig( placeholderPrefix = "${", placeholderSuffix = "}" ) ) @TestPropertySource("classpath:test-accounts.properties") @Test public void createCommentTest() throws Exception { // 测试逻辑 }
- 修改SQL脚本
把硬编码的值替换成${属性键}的占位符:
INSERT INTO permissions(id, name, created_date, modified_date) VALUES (1, 'ADMIN', current_timestamp, current_timestamp), (2, 'MODERATOR', current_timestamp, current_timestamp), (3, 'USER', current_timestamp, current_timestamp); INSERT INTO accounts(id, username, password, enabled, created_date, modified_date) VALUES (1, '${test.account.mickey.username}', '${test.account.mickey.password}', true, current_timestamp, current_timestamp), (2, '${test.account.minerva.username}', '${test.account.minerva.password}', true, current_timestamp, current_timestamp); INSERT INTO accounts_permissions(account_id, permission_id) VALUES (1, 3), (2, 3);
方法二:手动通过JdbcTemplate执行参数化SQL
如果需要更灵活的控制(比如动态生成数据),可以直接在Java代码中读取属性,用JdbcTemplate执行带参数的SQL:
- 注入依赖与属性
@Autowired private JdbcTemplate jdbcTemplate; @Value("${test.account.mickey.username}") private String mickeyUsername; @Value("${test.account.mickey.password}") private String mickeyPassword; @Value("${test.account.minerva.username}") private String minervaUsername; @Value("${test.account.minerva.password}") private String minervaPassword;
- 在测试方法中执行初始化
@Test public void createCommentTest() throws Exception { // 初始化权限数据 String insertPermissionSql = "INSERT INTO permissions(id, name, created_date, modified_date) VALUES (?, ?, current_timestamp, current_timestamp)"; jdbcTemplate.update(insertPermissionSql, 1, "ADMIN"); jdbcTemplate.update(insertPermissionSql, 2, "MODERATOR"); jdbcTemplate.update(insertPermissionSql, 3, "USER"); // 初始化账户数据 String insertAccountSql = "INSERT INTO accounts(id, username, password, enabled, created_date, modified_date) VALUES (?, ?, ?, ?, current_timestamp, current_timestamp)"; jdbcTemplate.update(insertAccountSql, 1, mickeyUsername, mickeyPassword, true); jdbcTemplate.update(insertAccountSql, 2, minervaUsername, minervaPassword, true); // 关联账户与权限 String insertAccountPermSql = "INSERT INTO accounts_permissions(account_id, permission_id) VALUES (?, ?)"; jdbcTemplate.update(insertAccountPermSql, 1, 3); jdbcTemplate.update(insertAccountPermSql, 2, 3); // 执行测试逻辑 }
方法三:数据库原生变量替换(依赖特定数据库)
部分数据库支持自身的变量语法,比如PostgreSQL的\set命令,但这种方法通用性差,仅适合固定使用某类数据库的场景:
\set mickey_username 'mickey_m' \set mickey_password '$2y$10$ZdqfOo1vwdHJJGnkmGrw/OUelZcU9ZfRFaX/RMN3XniXH96eTkB1e' INSERT INTO accounts(id, username, password, enabled, created_date, modified_date) VALUES (1, :mickey_username, :mickey_password, true, current_timestamp, current_timestamp);
注意:这种方式需要确保SQL执行器支持数据库的原生语法,Spring默认的脚本执行器可能无法解析,需额外配置。
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

