Spring ScriptUtils执行PostgreSQL脚本报错:未终止美元引号
解决Spring Boot中ScriptUtils执行PostgreSQL含美元引号函数脚本的问题
这个问题我之前也碰到过,根源是Spring的ScriptUtils默认的SQL语句分割逻辑和PostgreSQL的PL/pgSQL语法不兼容,具体原因和解决方法如下:
问题原因
你的SQL脚本使用了PostgreSQL特有的**美元引号($total$)来定义PL/pgSQL函数体,而ScriptUtils默认会以分号(;)**作为SQL语句的分隔符。当它扫描到函数内部的分号(比如SELECT count(*) into total FROM COMPANY;末尾的分号)时,会错误地将整个脚本截断成多个独立语句,导致美元引号没有被正确闭合,最终抛出"Unterminated dollar quote"异常。
解决方案
方案一:调整ScriptUtils的执行参数,适配PL/pgSQL脚本
你可以使用ScriptUtils.executeSqlScript的重载方法,自定义语句分隔符并关闭转义处理,避免误分割:
修改Java代码:
@Override public void run(String... args) throws Exception { try { @Cleanup Connection c = ds.getConnection(); // 配置适配PostgreSQL的脚本执行参数 ScriptUtils.executeSqlScript( c, resource, false, // 是否在错误时继续执行 true, // 是否忽略删除不存在对象的错误 "--", // 单行注释前缀 ";;", // 自定义语句分隔符(脚本中不会出现的符号) "/*", "*/", // 块注释的起始/结束标记 false // 关闭转义处理,适配PostgreSQL美元引号 ); } catch (Exception e) { e.printStackTrace(); } }
同时修改你的SQL脚本,在函数定义末尾添加自定义的分隔符;;:
CREATE OR REPLACE FUNCTION totalRecords () RETURNS integer AS $total$ declare total integer; BEGIN SELECT count(*) into total FROM COMPANY; RETURN total; END; $total$ LANGUAGE plpgsql;;
方案二:使用ResourceDatabasePopulator简化配置
如果觉得ScriptUtils的参数过于繁琐,可以用ResourceDatabasePopulator来封装配置,效果完全一致:
@Override public void run(String... args) throws Exception { try { @Cleanup Connection c = ds.getConnection(); ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.setContinueOnError(false); populator.setIgnoreFailedDrops(true); populator.setSeparator(";;"); // 自定义分隔符 populator.setEscapeProcessing(false);// 关闭转义处理 populator.addScripts(resource); populator.populate(c); } catch (Exception e) { e.printStackTrace(); } }
方案三:直接用JDBC执行完整脚本(绕过ScriptUtils)
如果上述方案仍有问题,你可以直接读取整个SQL脚本内容,作为单个SQL语句通过JDBC执行,彻底避免语句分割的问题:
@Override public void run(String... args) throws Exception { try { @Cleanup Connection c = ds.getConnection(); // 读取脚本的全部内容 String sqlScript = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream())); @Cleanup Statement stmt = c.createStatement(); stmt.execute(sqlScript); } catch (Exception e) { e.printStackTrace(); } }
这种方法适合复杂的PL/pgSQL脚本,完全绕过了ScriptUtils的分割逻辑,把整个函数定义作为一个完整命令执行。
额外注意事项
- 确保你的
COMPANY表已经存在,否则函数执行时会抛出表不存在的异常; - 确认Spring Boot的数据源配置正确(比如
application.properties中的spring.datasource.url、spring.datasource.username、spring.datasource.password),能正常连接到Docker中的PostgreSQL容器。
内容的提问来源于stack exchange,提问作者kulatamicuda
相关产品推荐
相关产品推荐

