java - 为 PostgreSQL 使用种子文件时 JPA 中的异常

标签 java spring postgresql hibernate jpa

我将 Spring Boot 2 与 JPA 结合使用,并将其留给 Hibernate 从我的实体创建我的数据库,这工作正常。现在我想添加一个 data.sql 文件,它将为我的数据库提供种子。我按如下方式配置了 JPA:

spring.jpa.properties.hibernate.hbm2ddl.import_files=data.sql

但是我在执行种子 SQL 时遇到了问题。在文件中我定义了几个函数,最后我正在执行它们:

CREATE OR REPLACE FUNCTION insert_timeout_configuration() RETURNS bigint AS $$
  DECLARE created_id bigint;

  BEGIN
    INSERT INTO timeout_configuration (id, version, timeout)
    VALUES (nextval('my_sequence'), 0, 300)
    RETURNING id INTO created_id;
    return created_id;
  END;
$$ language plpgsql;

CREATE OR REPLACE FUNCTION insert_url_configuration() RETURNS bigint AS $$
  DECLARE created_id bigint;

  BEGIN
    INSERT INTO url_configuration (id, version, my_url)
    VALUES (nextval('my_sequence'), 0,'http://localhost:8080/')
    RETURNING id INTO created_id;
    return created_id;
  END;
$$ language plpgsql;

DO $$
      INSERT INTO global_configuration(id, version, name, timeout_configuration_id, url_configuration_id)
VALUES (nextval('my_sequence'), 0, 'My global config', insert_timeout_configuration(), insert_url_configuration());

-- do some other code 
END
$$;
drop function insert_timeout_configuration();
drop function insert_url_configuration();

如果我在 PostgreSQL 控制台中执行相同的代码以从文件中读取它工作正常。但是,如果我通过 Spring 运行它,我会不断收到以下信息:

org.postgresql.util.PSQLException: Unterminated dollar quote started at position 0 in SQL $$ language plpgsql. Expected terminating $$
    at org.postgresql.core.Parser.checkParsePosition(Parser.java:1273) ~    [postgresql-42.2.4.jar:42.2.4]
    at org.postgresql.core.Parser.parseSql(Parser.java:1172) ~[postgresql-    42.2.4.jar:42.2.4]
    at org.postgresql.core.Parser.replaceProcessing(Parser.java:1124) ~    [postgresql-42.2.4.jar:42.2.4]
at     org.postgresql.core.CachedQueryCreateAction.create(CachedQueryCreateAction.java:41) ~[postgresql-42.2.4.jar:42.2.4]
at org.postgresql.core.QueryExecutorBase.createQueryByKey(QueryExecutorBase.java:314) ~[postgresql-42.2.4.jar:42.2.4]
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:285) ~[postgresql-42.2.4.jar:42.2.4]
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:270) ~    [postgresql-42.2.4.jar:42.2.4]
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:266) ~    [postgresql-42.2.4.jar:42.2.4]
    at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95) ~    [HikariCP-2.7.9.jar:?]
at         com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) ~    [HikariCP-2.7.9.jar:?]
    at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(Generat    ionTargetToDatabase.java:54) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    ... 33 more
[DEBUG] 2018-09-07 21:09:43.325 [main] SQL - CREATE OR REPLACE FUNCTION insert_url_configuration() RETURNS bigint AS $$
Hibernate: CREATE OR REPLACE FUNCTION insert_url_configuration() RETURNS bigint AS $$
[WARN ] 2018-09-07 21:09:43.325 [main] ExceptionHandlerLoggedImpl - GenerationTarget encountered exception accepting command : Error executing DDL 
via JDBC Statement

我正在使用 PostgreSQL 9.5 和 Spring Boot 2.0.3。我读到函数定义中的分隔符 $$ 无法正确解析,但我找不到解决方法。我尝试使用简单的 '' 而不是 $$ 并在任何地方转义单引号,但这也不起作用。

最佳答案

问题不在于语法,因为语法可以完美地与 flyway 一起使用或直接在 PostgreSQL CLI 中使用。问题出在 Hibernate 上,特别是在解析导入文件时。 Hibernate 的工作方式是它单独执行文件中的每个表达式,而不是将整个内容作为单个表达式执行。我试图将所有函数定义放在一行中并且它起作用了,但它不可读。于是我发现Hibernate有一个配置告诉它表达式可以是多行的,但是$$分隔符在多行的时候还是无法识别。

因此解决方案是使用 ' 定界符定义命令,然后在需要的地方使用额外的 ' 转义单引号。

解决方案是将 spring.jpa.properties.hibernate.hbm2ddl.import_files_sql_extractor 设置为使用 org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor。 MultipleLinesSqlCommandExtractor 从多行中提取 SQL 表达式,并在出现分号时停止。那是表达式的结尾。通过将函数体包裹在单引号字符串中,Hibernate 会将包裹视为单行。

数据.sql

CREATE OR REPLACE FUNCTION insert_timeout_configuration() RETURNS bigint AS '
  DECLARE created_id bigint;

  BEGIN
    INSERT INTO timeout_configuration (id, version, timeout)
    VALUES (nextval(''my_sequence''), 0, 300)
    RETURNING id INTO created_id;
    return created_id;
  END;
' language plpgsql;

CREATE OR REPLACE FUNCTION insert_url_configuration() RETURNS bigint AS '
  DECLARE created_id bigint;

  BEGIN
    INSERT INTO url_configuration (id, version, my_url)
    VALUES (nextval(''my_sequence''), 0,''http://localhost:8080/'')
    RETURNING id INTO created_id;
    return created_id;
  END;
' language plpgsql;

DO '
      INSERT INTO global_configuration(id, version, name, timeout_configuration_id, url_configuration_id)
      VALUES (nextval(''my_sequence''), 0, ''My global config'', insert_timeout_configuration(), insert_url_configuration());

-- do some other code 
END
';
drop function insert_timeout_configuration();
drop function insert_url_configuration();

我必须始终牢记在表达式中转义单引号,但现在我可以拥有一个更易于阅读的种子文件。

关于java - 为 PostgreSQL 使用种子文件时 JPA 中的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52228470/

相关文章:

java - Java 中的 Collection 和 Set 接口(interface)有什么区别?

java - 如何在 Android 4.X 中以编程方式导入全局 CA 证书?

java - 这两种方法在 java 中 sleep 执行的区别

java - Jackson 从 ISO DateTime 反序列化 Joda LocalDate

java - Springfox Swagger命令属性如何配置?

java - Spring bean 实现两个接口(interface)

ruby-on-rails - 使用 Postgres 全文搜索来搜索完全匹配的最佳方法是什么?

java - Jackson 的@JsonView、@JsonFilter 和 Spring

database - Oracle Schema 疑问?

postgresql - Postgres :10 in docker swarm cluster. 数据库系统被关闭