如何在Gradle的Flyway扩展任务后执行PL/pgSQL脚本?
问题描述
我有一个采用Gradle做依赖管理、连接Postgres数据库的Java Spring应用。测试时需频繁执行迁移、调整架构,因此经常清理数据库。Gradle的flywayClean任务无法完全清理数据库,自定义用户、模式和角色需通过PL/pgSQL脚本手动清理。由于涉及大量模式,我已将flywayClean扩展为单个模式清理任务及总任务dbClean。现在希望在执行dbClean任务后运行清理脚本,但遇到两个问题:
- 使用Groovy SQL实例执行脚本时,出现“找不到合适驱动”的错误;
- 使用Flyway的afterClean回调可执行脚本,但会触发所有继承flywayClean的任务,不符合需求。
解决方案
核心思路
通过Gradle的finalizedBy机制,让总任务dbClean执行完成后单独触发自定义清理脚本任务,避免影响单个模式的清理任务;同时通过两种方式解决驱动问题:
方式一:使用Groovy SQL执行脚本(解决驱动问题)
首先在build.gradle的buildscript中引入Postgres驱动和Groovy SQL依赖,确保Gradle能找到JDBC驱动:
buildscript { dependencies { // 替换为你的Postgres驱动版本 classpath 'org.postgresql:postgresql:42.7.3' // 匹配你的Groovy版本 classpath 'org.codehaus.groovy:groovy-sql:3.0.17' } } plugins { id 'org.flywaydb.flyway' version '9.22.3' // 替换为你的Flyway版本 id 'java' } // 基础Flyway配置 flyway { url = 'jdbc:postgresql://localhost:5432/your_db' user = 'db_user' password = 'db_password' } // 单个模式清理任务定义(假设你已有类似代码) def schemaNames = ['schema1', 'schema2', 'schema3'] schemaNames.each { schema -> tasks.create("flywayClean${schema.capitalize()}", FlywayCleanTask) { schemaNames = [schema] url = flyway.url user = flyway.user password = flyway.password } } // 总清理任务dbClean task dbClean { dependsOn schemaNames.collect { "flywayClean${it.capitalize()}" } // 指定dbClean完成后执行自定义脚本任务 finalizedBy runCustomCleanScript } // 自定义Groovy任务执行PL/pgSQL脚本 task runCustomCleanScript(type: Groovy) { doLast { def sql = groovy.sql.Sql.newInstance( flyway.url, flyway.user, flyway.password, 'org.postgresql.Driver' ) try { // 读取本地清理脚本文件 def scriptContent = file('./scripts/db_cleanup.sql').text // 执行脚本(复杂PL/pgSQL可能需要拆分语句,或改用executeUpdate) sql.execute(scriptContent) } finally { sql.close() } } }
方式二:调用psql命令执行脚本(完全绕开JDBC驱动问题)
如果Groovy SQL的驱动问题难以调试,直接用Gradle的Exec任务调用Postgres自带的psql命令执行脚本,更适合复杂的PL/pgSQL逻辑:
// 替换方式一中的runCustomCleanScript任务,改用Exec任务 task runCustomCleanScriptViaPsql(type: Exec) { commandLine 'psql', '-h', 'localhost', // 数据库主机 '-p', '5432', // 端口 '-U', flyway.user, // 数据库用户 '-d', flyway.url.split('/').last(), // 从URL中提取数据库名 '-f', './scripts/db_cleanup.sql' // 脚本路径 // 设置环境变量避免密码输入提示 environment 'PGPASSWORD', flyway.password } // 修改dbClean任务的finalizedBy指向这个任务 task dbClean { dependsOn schemaNames.collect { "flywayClean${it.capitalize()}" } finalizedBy runCustomCleanScriptViaPsql }
关键说明
finalizedBy只会在dbClean任务成功执行完成后触发后续任务,单个模式的flywayCleanXXX任务不会触发脚本执行,完全符合需求。- 方式二更稳定,尤其是执行包含事务、存储过程的复杂PL/pgSQL脚本时,psql的兼容性更好。
内容的提问来源于stack exchange,提问作者Eriol
相关产品推荐
相关产品推荐

