在 spring boot schema.sql 文件中执行过程的问题
Posted
技术标签:
【中文标题】在 spring boot schema.sql 文件中执行过程的问题【英文标题】:Issue with executing procedure in spring boot schema.sql file 【发布时间】:2015-12-30 11:00:57 【问题描述】:我在我的 Spring 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 工作台中执行此过程并获得成功的结果。
那么,有谁知道这个问题的原因,请告诉我?
【问题讨论】:
看这里如何创建你不需要的程序DELIMITER
***.com/questions/31769551
【参考方案1】:
这是我发现效果很好的解决方案,尽管它并不理想,因为您必须更改 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,不适用于 Spring Boot 数据库初始化。删除 DELIMITER 命令后,Spring Boot 仍然会抛出异常,因为它无法理解存储过程中的 ;
字符不是单独的语句,因此您必须更改数据源分隔符属性作为解决方法。
【讨论】:
【参考方案2】:解决方法如下:
将您的spring.datasource.separator
属性设置为^^^ END OF SCRIPT ^^^
,Spring Boot 会将您的整个 schema.sql 脚本作为一条语句执行。
原因如下:
Spring 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 语句分隔符:“^^^ END OF SCRIPT ^^^”。
此值可以作为分隔符提供给 executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) 以表示 SQL 脚本包含单个语句(可能跨越多行)而没有显式语句分隔器。请注意,这样的脚本实际上不应包含此值;它只是一个虚拟语句分隔符。
【讨论】:
【参考方案3】:使用适当的分隔符 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);
【讨论】:
【参考方案4】:上述答案在 2022 年对我来说都不起作用,但它们为我提供了一个焦点来提出我自己的工作解决方案。我在类似问题下发布了我的答案,但为了完整起见,可以为您节省一些额外的点击次数,我也在此处提供。
我也遇到过这个问题,现在我找到了解决方案。 其实是3!因此,您可以选择适合您需求的任何一种。但它的关键是正确格式化的String
,它代表创建SP 的DB 脚本。
我还没有弄清楚如何在一个 SQL 脚本/字符串中结合删除和创建 SP,但无论如何我对这些解决方案很满意。
仅供参考:
不需要在类或方法级别上进行特殊注释或设置 我正在使用Spring FW,它简化了事情,所以不需要对所有内容进行底层实现 在搜索并尝试了各种绝对有效的“提示”后,以下资源帮助我找到了那些确实有效的解决方案: Execute SQL file from Spring JDBC Template *** 上的类似问题 Spring and MySQL stored procedure,我实际上已经在那里发布了我的答案 但最有用的是官方 Oracle tutorial 上的 JavaSE 和 JDBC 和其他Java/JDBC tutorial 在这里您可以看到格式化sql JavaString
最重要的一点
解决方案 1:使用 JdbcTemplate
和 Java String
在格式良好的String
sql 脚本中包含尾随空格非常重要
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找到
【讨论】:
以上是关于在 spring boot schema.sql 文件中执行过程的问题的主要内容,如果未能解决你的问题,请参考以下文章
在 spring boot schema.sql 文件中执行过程的问题
spring boot2.0后配置自动执行schema.sql无效
无法在 schema.sql 中为 Spring Boot 应用程序使用“DROP TABLE IF EXISTS”
Spring Boot 无法运行依赖于 spring-cloud-starter-config 的 schema.sql