我正在将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。我读到函数定义中的定界符$$
无法正确解析,但是我找不到解决方法。我尝试使用$$
代替简单的''
并在所有地方都使用单引号,但这还是行不通的。
答案 0 :(得分:2)
问题不在于语法,因为语法可以完美地与flyway一起使用,也可以直接在PostgreSQL CLI中使用。问题出在Hibernate,特别是解析导入文件。 Hibernate的工作方式是单独执行文件中的每个表达式,而不是将整个内容作为单个表达式执行。我试图将所有函数定义放在一行中,并且可以正常工作,但是它不可读。因此,我发现Hibernate有一种配置可以告诉它表达式可以多行显示,但是在多行中使用$$
分隔符时仍然无法识别。
因此,解决方案是用'
分隔符定义命令,然后在需要的地方使用附加的'
转义单引号。
解决方案是将spring.jpa.properties.hibernate.hbm2ddl.import_files_sql_extractor
设置为使用org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor
。 MultipleLinesSqlCommandExtractor从多行提取SQL表达式,并在出现分号时停止。到此为止。通过将函数的主体包装在单引号字符串中,Hibernate将把该包装视为单行。
data.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();
我必须始终谨记要在表达式中转义单引号,但现在我可以拥有一个更易于理解的种子文件。