无法将 schema.sql 中的 "DROP TABLE IF EXISTS" 用于 Spring 启动应用程序
Unable to use "DROP TABLE IF EXISTS" in schema.sql for a Spring Boot application
我的 schema.sql
中的 DROP/CREATE 个表格需要帮助
设置:
- 甲骨文 XE
- Spring Boot v1.4.0
- Java1.8
当我在 schema.sql 中有以下条目时:
DROP TABLE table_a;
CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100));
我得到异常
DROP TABLE table_a; nested exception is java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
当我查找有关如何在 Oracle 中执行 DROP TABLE IF EXISTS 的帮助时,我得到的最佳答案如下(适用于 SQLDeveloper):
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE table_a';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100)
)';
END;
但是,上面的代码抛出以下异常:
2016-08-10 14:55:36.232 INFO 9032 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from URL [file:/C:/projects/project_a/target/classes/schema.sql]
2016-08-10 14:55:36.286 WARN 9032 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.dataSource; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$NonEmbeddedConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceInitializer': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of URL [file:/C:/projects/project_a/target/classes/schema.sql]: BEGIN EXECUTE IMMEDIATE 'DROP TABLE table_a'; nested exception is java.sql.SQLException: ORA-06550: line 1, column 44:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
- & = - + ; < / > at in is mod remainder not rem return
returning <> or != or ~= >= <= <> and or
like like2 like4 likec between into using || multiset bulk
member submultiset
有人有更优雅的方法来处理 Spring 引导中的 DROP/CREATE Oracle 表吗?
我认为这确实是一个 SQL 问题。您可以使用sql exist 语句查看系统表,看看您的对象是否存在。
Oracle 鼓励您使用全局临时 tables,而不是删除并重新创建 tables。全局临时 table 的优点是您不必通过删除 table 的例程来重新创建它。
现在,关于全局临时 table 的事情是其中的数据仅对创建它的会话可见,并且当 A) 事务提交或 B ) 会话断开连接 - 您可以选择在创建 table 时删除数据的时间,但数据不会持续很长时间,也不会对连接到数据库的每个人可见。它适用于 "scratchpad" tables,其中应用程序需要临时将数据放入 table,在给定会话中使用它,然后删除它。如果这符合您的预期用途,这将是一个不错的选择。
要将 table 创建为全局临时文件 table,您需要指定以下内容:
CREATE GLOBAL TEMPORARY TABLE table_a
(id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100))
ON COMMIT PRESERVE ROWS;
(作为旁注 - 您应该养成在 Oracle 中使用 VARCHAR2 而不是 VARCHAR 的习惯。VARCHAR 是 Oracle 不正确支持的 ANSI 类型。在 Oracle 中,VARCHAR 目前是 VARCHAR2 的同义词,但是有传言说有一天 Oracle 将更改 VARCHAR,因此它完全符合 ANSI,如果您在 table 中使用它,您的数据库的行为可能会在没有警告的情况下悄悄改变。所以这是你的警告:-) .
祝你好运。
创建存储过程
create or replace procedure recreate_table
(i_table_name in varchar2, i_create_stmt in varchar2)
is
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE '||upper(i_table_name);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
EXECUTE IMMEDIATE i_create_stmt;
END;
然后你的schema.sql可以使用SQL语句:
call recreate_table('TABLE_A','CREATE TABLE TABLE_A (ID NUMBER, VAL VARCHAR2(10))');
而不是包括 PL/SQL
您尚未显示 Java 代码,但从堆栈跟踪来看,您似乎正在调用 ScriptUtil's executeSqlScript()
method,它使用了默认的分号语句分隔符。
它没有将 PL/SQL 块识别为一个单独的单元,而是试图 运行 将第一个分号之前的所有内容作为独立的 SQL 语句 - 这是无效并导致您看到的错误。
您可以使用 the version of executeSqlScript()
来覆盖默认值并改用 /
:
public static void executeSqlScript(Connection connection,
EncodedResource resource,
boolean continueOnError,
boolean ignoreFailedDrops,
String commentPrefix,
String separator,
String blockCommentStartDelimiter,
String blockCommentEndDelimiter)
throws ScriptException
separator - the script statement separator; defaults to ";" if not specified and falls back to "\n" as a last resort; may be set to "^^^ END OF SCRIPT ^^^" to signal that the script contains a single statement without a separator
这意味着脚本中的所有 SQL 语句都必须使用 /
分隔符而不是分号:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE table_a';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100)
)
/
...
如评论中所述,您的原始块无论如何都不太正确; create
不需要通过 PL/SQL 完成,即使 drop
需要。
但该方法也有一个 ignoreFailedDrops
标志,它似乎完全符合您的要求(不过我无法对其进行测试):
ignoreFailedDrops - whether or not to continue in the event of specifically an error on a DROP statement
如果您使用该版本并为该标志传递 true,则不需要 PL/SQL 包装器;您可以保留分号分隔符并恢复为:
DROP TABLE table_a;
CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100)
);
...
如果您的模式脚本包含任何其他 PL/SQL - 触发器、包等 - 那么您仍然需要切换到使用斜杠分隔符(或您选择的任何其他分隔符;斜杠是传统的虽然)一切。
这是旧线程,但我在最新的 Spring Boot - 1.5.4 中偶然发现了同样的问题,我想我已经找到了答案(也感谢上面的 Alex Poole)。默认情况下 Spring 引导使用 属性
spring.datasource.separator=;
所以你可以看到';'用作 SQL 命令的分隔符。因此,在尝试将 PL/SQL 过程放入 'schema.sql' 的过程中,Oracle DB 仅首先收到 SQL-command 并尝试将其写入 DB。所以有代码:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE table_a';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
在数据库中只有 BEGIN 和 EXECUTE IMMEDIATE 'DROP TABLE table_a';正在存储-您可以在例如中看到。 SQL 开发者。将分隔符更改为例如。 ';;'有帮助 - 并且还在 SQL 代码中使用它而不是“;”。我不建议使用“/”作为分隔符,因为这是用于创建多行-SQL-注释的符号,所以当有人在 SQL 文件中使用它们时可能会导致问题。
我的 schema.sql
中的 DROP/CREATE 个表格需要帮助设置:
- 甲骨文 XE
- Spring Boot v1.4.0
- Java1.8
当我在 schema.sql 中有以下条目时:
DROP TABLE table_a;
CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100));
我得到异常
DROP TABLE table_a; nested exception is java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
当我查找有关如何在 Oracle 中执行 DROP TABLE IF EXISTS 的帮助时,我得到的最佳答案如下(适用于 SQLDeveloper):
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE table_a';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
EXECUTE IMMEDIATE 'CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100)
)';
END;
但是,上面的代码抛出以下异常:
2016-08-10 14:55:36.232 INFO 9032 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from URL [file:/C:/projects/project_a/target/classes/schema.sql] 2016-08-10 14:55:36.286 WARN 9032 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.dataSource; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$NonEmbeddedConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceInitializer': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of URL [file:/C:/projects/project_a/target/classes/schema.sql]: BEGIN EXECUTE IMMEDIATE 'DROP TABLE table_a'; nested exception is java.sql.SQLException: ORA-06550: line 1, column 44: PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
- & = - + ; < / > at in is mod remainder not rem return returning <> or != or ~= >= <= <> and or like like2 like4 likec between into using || multiset bulk member submultiset
有人有更优雅的方法来处理 Spring 引导中的 DROP/CREATE Oracle 表吗?
我认为这确实是一个 SQL 问题。您可以使用sql exist 语句查看系统表,看看您的对象是否存在。
Oracle 鼓励您使用全局临时 tables,而不是删除并重新创建 tables。全局临时 table 的优点是您不必通过删除 table 的例程来重新创建它。
现在,关于全局临时 table 的事情是其中的数据仅对创建它的会话可见,并且当 A) 事务提交或 B ) 会话断开连接 - 您可以选择在创建 table 时删除数据的时间,但数据不会持续很长时间,也不会对连接到数据库的每个人可见。它适用于 "scratchpad" tables,其中应用程序需要临时将数据放入 table,在给定会话中使用它,然后删除它。如果这符合您的预期用途,这将是一个不错的选择。
要将 table 创建为全局临时文件 table,您需要指定以下内容:
CREATE GLOBAL TEMPORARY TABLE table_a
(id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100))
ON COMMIT PRESERVE ROWS;
(作为旁注 - 您应该养成在 Oracle 中使用 VARCHAR2 而不是 VARCHAR 的习惯。VARCHAR 是 Oracle 不正确支持的 ANSI 类型。在 Oracle 中,VARCHAR 目前是 VARCHAR2 的同义词,但是有传言说有一天 Oracle 将更改 VARCHAR,因此它完全符合 ANSI,如果您在 table 中使用它,您的数据库的行为可能会在没有警告的情况下悄悄改变。所以这是你的警告:-) .
祝你好运。
创建存储过程
create or replace procedure recreate_table
(i_table_name in varchar2, i_create_stmt in varchar2)
is
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE '||upper(i_table_name);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
EXECUTE IMMEDIATE i_create_stmt;
END;
然后你的schema.sql可以使用SQL语句:
call recreate_table('TABLE_A','CREATE TABLE TABLE_A (ID NUMBER, VAL VARCHAR2(10))');
而不是包括 PL/SQL
您尚未显示 Java 代码,但从堆栈跟踪来看,您似乎正在调用 ScriptUtil's executeSqlScript()
method,它使用了默认的分号语句分隔符。
它没有将 PL/SQL 块识别为一个单独的单元,而是试图 运行 将第一个分号之前的所有内容作为独立的 SQL 语句 - 这是无效并导致您看到的错误。
您可以使用 the version of executeSqlScript()
来覆盖默认值并改用 /
:
public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException
separator - the script statement separator; defaults to ";" if not specified and falls back to "\n" as a last resort; may be set to "^^^ END OF SCRIPT ^^^" to signal that the script contains a single statement without a separator
这意味着脚本中的所有 SQL 语句都必须使用 /
分隔符而不是分号:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE table_a';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100)
)
/
...
如评论中所述,您的原始块无论如何都不太正确; create
不需要通过 PL/SQL 完成,即使 drop
需要。
但该方法也有一个 ignoreFailedDrops
标志,它似乎完全符合您的要求(不过我无法对其进行测试):
ignoreFailedDrops - whether or not to continue in the event of specifically an error on a DROP statement
如果您使用该版本并为该标志传递 true,则不需要 PL/SQL 包装器;您可以保留分号分隔符并恢复为:
DROP TABLE table_a;
CREATE TABLE table_a
(
id VARCHAR(5) PRIMARY KEY,
name VARCHAR(100)
);
...
如果您的模式脚本包含任何其他 PL/SQL - 触发器、包等 - 那么您仍然需要切换到使用斜杠分隔符(或您选择的任何其他分隔符;斜杠是传统的虽然)一切。
这是旧线程,但我在最新的 Spring Boot - 1.5.4 中偶然发现了同样的问题,我想我已经找到了答案(也感谢上面的 Alex Poole)。默认情况下 Spring 引导使用 属性
spring.datasource.separator=;
所以你可以看到';'用作 SQL 命令的分隔符。因此,在尝试将 PL/SQL 过程放入 'schema.sql' 的过程中,Oracle DB 仅首先收到 SQL-command 并尝试将其写入 DB。所以有代码:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE table_a';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
在数据库中只有 BEGIN 和 EXECUTE IMMEDIATE 'DROP TABLE table_a';正在存储-您可以在例如中看到。 SQL 开发者。将分隔符更改为例如。 ';;'有帮助 - 并且还在 SQL 代码中使用它而不是“;”。我不建议使用“/”作为分隔符,因为这是用于创建多行-SQL-注释的符号,所以当有人在 SQL 文件中使用它们时可能会导致问题。