无法将 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 个表格需要帮助

设置:

当我在 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 文件中使用它们时可能会导致问题。