Spring JDBC 在运行 SQL 脚本时无法处理 DECLARE 语句
Posted
技术标签:
【中文标题】Spring JDBC 在运行 SQL 脚本时无法处理 DECLARE 语句【英文标题】:Spring JDBC cannot handle DECLARE statement when running SQL Script 【发布时间】:2020-02-12 12:11:24 【问题描述】:我有一个带有 Oracle SQL 数据库的 Spring CRUD 应用程序。
我有几个集成测试并使用@Sql
注释运行.sql 文件,这些文件在运行测试之前将数据库置于所需状态。到目前为止,我的任何 .sql 脚本都没有问题,尽管它们都是非常简单的 INSERT 和 DROP 语句。
我现在正在尝试设置一个场景,其中数据库在特定表中保存数千条记录。我不关心内容,只关心记录的数量。
为了复制这个场景,我编写了以下 SQL 脚本:
DECLARE
id integer := 1;
BEGIN
WHILE id <= 3000
LOOP
INSERT INTO USER (ID, FIRST_NAME, LAST_NAME, AGE)
VALUES (id, 'John', 'Smith', 32);
id := id + 1;
END LOOP;
END;
我已经使用 IntelliJ 运行了这个脚本,它按预期工作。
但是,当我将脚本放入@Sql 注释时,我遇到了以下错误:
org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of class path resource [sql/setUp.sql]: DECLARE id integer := 1; nested exception is java.sql.SQLException: ORA-06550: line 1, column 23:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
* & = - + ; < / > at in is mod remainder not rem
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || multiset member submultiset
我不知道如何解决;我遇到过类似的问题,解决方案只是缺少分号,但这里似乎并非如此。 更令人费解的是,我尝试通过@Sql 注解运行以下脚本:
BEGIN
INSERT INTO USER (ID, FIRST_NAME, LAST_NAME, AGE)
VALUES (1, 'John', 'Smith', 32);
END;
这产生了以下错误,这更没有意义:
org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of class path resource [sql/test.sql]: BEGIN INSERT INTO USER (ID, FIRST_NAME, LAST_NAME, AGE) VALUES (1, 'John', 'Smith', 32); nested exception is java.sql.SQLException: ORA-06550: line 1, column 87:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
;
最值得注意的是删除 BEGIN 和 END 语句(只留下 INSERT INTO 语句)是可行的。
那么 Spring JDBC 是否根本不支持 BEGIN/END 块?我不知道的预测试 @Sql 注释是否有限制?还是我错过了一些非常明显的事情?
【问题讨论】:
请提供minimal reproducible example。问题似乎是,无论您在做什么都是在;
上拆分以发送单个语句(因为 JDBC 本身仅支持单个语句执行)。要执行这样的脚本,通常需要以某种方式切换或禁用语句终止符才能发送整个内容。这意味着您可能需要微调@Sql
的@SqlConfig
(例如separator
的值)。
@MarkRotteveel:Oracle JDBC 驱动程序确实支持运行匿名 PL/SQL 块,因为它实际上是一条语句。可能是 Spring 试图将 看起来 的多个语句分开
@a_horse_with_no_name 是的,@Sql
的 default 将在 ;
上拆分。所以就 Spring 而言,在遇到每个 ;
之后,都会启动一个新语句。
不是答案,因为我不确定它是否正确,但您可以尝试在 END;
语句之后添加另一行,第一列中只有一个 /
字符。
@MarkRotteveel 感谢您的建议,我找到了解决方案。非常感谢!
【参考方案1】:
感谢@MarkRotteveel 的建议,我找到了解决这个问题的方法。
JDBC 确实在运行脚本的每一行,就好像它是一个单独的语句,这与语句块不兼容。这是因为 separator
默认设置为 ;
符号,导致 JDBC 将每个以 ;
结尾的代码块视为单独的脚本。我将此分隔符设置为 EOF 符号,这导致 JDBC 将整个文件视为单个脚本。
我的原始测试代码如下所示:
@Sql(scripts = "sql/cleanup.sql", "sql/setUp.sql")
public void testWithThousandsOfRecords()
// do tests
我的测试代码现在看起来像这样:
@Sql(scripts = "sql/cleanup.sql")
@Sql(scripts = "sql/setUp.sql", config = @SqlConfig(separator = ScriptUtils.EOF_STATEMENT_SEPARATOR))
public void testWithThousandsOfRecords()
// do tests
请注意,我必须将我的清理和设置脚本分开,因为更改分隔符可能会破坏一些以前工作的脚本。
【讨论】:
如果您需要在脚本中有多个语句,请考虑使用自定义语句终止符(例如#
而不是;
),而不是使用EOF_STATEMENT_SEPARATOR
。然后您可以使用#
结束语句,同时您可以继续在PL/SQL 块中使用;
。
在这种情况下你会怎么做?以上是关于Spring JDBC 在运行 SQL 脚本时无法处理 DECLARE 语句的主要内容,如果未能解决你的问题,请参考以下文章
Sql Server JDBC无法在wildfly AS上运行
Spring JDBC 无法加载 JDBC 驱动程序类 [oracle.jdbc.driver.OracleDriver]