spring 在Sping Boot schema.sql文件中执行过程时出现问题

zi8p0yeb  于 2022-11-28  发布在  Spring
关注(0)|答案(5)|浏览(264)

我正在我的Sping Boot 应用程序中使用schema.sql文件到CREATE/DROP表,它工作正常。
但是当我添加了更改表的过程时:

DELIMITER $$
CREATE PROCEDURE Alter_Table()
BEGIN
 IF NOT EXISTS( SELECT NULL
            FROM INFORMATION_SCHEMA.COLUMNS
           WHERE table_name = 'test_table'
             AND table_schema = 'test'
             AND column_name = 'cc_test_id')  THEN

  alter table test_table add cc_test_id VARCHAR(128) NOT NULL;

END IF;
END $$

call Alter_Table;

我收到:

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException exception.

但是,在MySQL工作台中执行此过程后,结果成功。

所以,应该有人知道这个问题的原因是什么,让我知道吗?

bprjcwpo

bprjcwpo1#

下面是我发现的一个很好的解决方案,尽管它并不理想,因为您必须更改SQL脚本。
application.properties文件中更改DataSource分隔符属性:

spring.datasource.separator=^;

然后更新schema.sql文件,使其如下所示:

CREATE PROCEDURE Alter_Table()
BEGIN
 IF NOT EXISTS( SELECT NULL
            FROM INFORMATION_SCHEMA.COLUMNS
           WHERE table_name = 'test_table'
             AND table_schema = 'test'
             AND column_name = 'cc_test_id')  THEN

  alter table test_table add cc_test_id VARCHAR(128) NOT NULL;

END IF;
END ^;

call Alter_Table ^;

DELIMITER命令仅适用于MySQL CLI客户端和Workbench,不适用于Sping Boot 数据库初始化。删除DELIMITER命令后,Spring Boot仍将抛出异常,因为它无法理解存储过程中的;字符不是单独的语句,因此您必须更改数据源分隔符属性作为解决方法。

7tofc5zh

7tofc5zh2#

以下是解决方法:
将您的spring.datasource.separator属性设置为^^^ END OF SCRIPT ^^^,Sping Boot 会将整个schema.sql脚本作为单个语句执行。
原因如下:
Sping Boot 将schema.sql脚本拆分为语句,然后将每个语句单独发送到数据库以供执行。默认情况下,脚本以分号分隔,因为这是spring.datasource.separator属性(per the documentation)的默认值。这会导致schema.sql被拆分,执行的第一个语句如下:

DELIMITER $$
CREATE PROCEDURE Alter_Table()
BEGIN
 IF NOT EXISTS( SELECT NULL
            FROM INFORMATION_SCHEMA.COLUMNS
           WHERE table_name = 'test_table'
             AND table_schema = 'test'
             AND column_name = 'cc_test_id')  THEN

  alter table test_table add cc_test_id VARCHAR(128) NOT NULL

这是无效的SQL,因为美元引号在语句中从不终止。
org.springframework.jdbc.datasource.init.ScriptUtils.EOF_STATEMENT_SEPARATOR的javadoc很好地解释了我建议的解决方案是如何工作的:
文件结束(EOF)SQL语句分隔符:“^^^脚本结束^^^"。
此值可以作为分隔符提供给executeSqlScript(Connection,EncodedResource,boolean,boolean,String,String,String,String),以表示SQL脚本包含没有显式语句分隔符的单个语句(可能跨越多行)。请注意,此类脚本实际上不应包含此值;它只是一个虚拟语句分隔符。

sgtfey8w

sgtfey8w3#

使用适当的分隔符EOF_STATEMENT_SEPARATOR自定义数据库填充器可解决此问题:

@Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
    var resource = new ResourceDatabasePopulator(new ClassPathResource("schema.sql"));
    resource.setSeparator(ScriptUtils.EOF_STATEMENT_SEPARATOR);
    var populator = new CompositeDatabasePopulator();
    populator.addPopulators(resource);
}
3qpi33ja

3qpi33ja4#

以上答案在2022年对我来说都不管用,但它们提供了一个焦点,让我可以提出自己的解决方案。我在类似的问题下发布了我的答案,但为了完整起见,为了保存你一些额外的点击,我也在这里提供了它。
我也曾经遇到过这个问题,现在我找到了一个解决方案。实际上是3个!所以你可以选择任何一个适合你的需要。但它的关键是正确格式化的String,它代表了创建SP的DB脚本。我还没有弄清楚如何在一个SQL脚本/字符串中合并删除和创建SP,但我对这些解决方案很满意。
仅供参考:

  • 不需要在类或方法级别上进行特殊的注解或设置
  • 我使用的是Spring FW,它简化了事情,因此不需要执行所有底层实现
  • 在搜索和尝试了各种绝对有效的“技巧”后,以下资源帮助我找到了那些肯定有效的解决方案:
  • Execute SQL file from Spring JDBC Template
  • stackoverflow上类似的问题Spring and MySQL stored procedure,我实际上已经在那里发布了我的答案
  • 但最有用的是JavaSE和JDBC上的官方Oracle tutorial
  • 和其他Java/JDBC tutorial,您可以在其中看到格式化sql Java String的最重要部分

解决方案1:使用JdbcTemplate和Java String

在格式良好的Stringsql脚本中保留尾随空格非常重要

String sqlSP = "CREATE PROCEDURE test_stored_proc (IN pInstanceId varchar(255)) " +
                "BEGIN " +
                    "SELECT * FROM vw_subscriptions WHERE instanceId = pInstanceId; " +
                "END";

        jdbcTemplate.execute(sqlSP);

解决方案2:使用ResourceDatabasePopulator & ClassPathResource将SQL脚本加载到文件中

ClassPathResource resource = new ClassPathResource("/data/create-sp_TEST_STORED_PROC.sql");
        jdbcTemplate.execute("DROP procedure IF EXISTS `test_stored_proc`;");

        ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(resource);
        databasePopulator.setSeparator(ScriptUtils.EOF_STATEMENT_SEPARATOR);
        databasePopulator.execute(testSetupDataSource);

解决方案3:使用ScriptUtils & ClassPathResource将SQL脚本加载到文件中

ClassPathResource resource = new ClassPathResource("/data/create-sp_TEST_STORED_PROC.sql");
        jdbcTemplate.execute("DROP procedure IF EXISTS `test_stored_proc`;");

        ScriptUtils.executeSqlScript(Objects.requireNonNull(testSetupDataSource).getConnection(), new EncodedResource(resource),
                false, false,
                ScriptUtils.DEFAULT_COMMENT_PREFIX, ScriptUtils.EOF_STATEMENT_SEPARATOR,
                ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER, ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER);

create-sp_TEST_STORED_PROC.sql脚本的内容

对于溶液#1和#2,适用与第一种溶液相同的规则:

    • 空格 * 字符的格式和存在非常重要,尤其是每行末尾的额外尾随空格。

因此,下面的代码由我的vim设置存档,以表示空白字符

CREATE·PROCEDURE·test_stored_proc·(IN·pInstanceId·varchar(255))~¬
BEGIN~¬
–→SELECT·*·FROM·vw_subscriptions·WHERE·instanceId·=·pInstanceId;~¬
END;~¬

我相信它在内部表现为一条线:CREATE PROCEDURE test_stored_proc (IN pInstanceId varchar(255)) BEGIN SELECT * FROM vw_subscriptions WHERE instanceId = pInstanceId; END
几乎完整的源代码可以在我的GitHub上找到

4zcjmb1e

4zcjmb1e5#

如果您尝试为SpringBootTest加载脚本,@Sql@SqlConfig将起作用。

@Sql(
    "/db/integration/stored-procedures.sql",
    config = SqlConfig(separator = "$$")
)

在sql脚本中,应该删除DELIMITER $$语句。如下所示:

CREATE PROCEDURE <sp_name>()
<your owesome definition>
END $$

如果脚本文件只包含一个CREATE PROCEDURE语句,则separator=EOF_STATEMENT_SEPARATOR语句将正常。

相关问题