使用 Spring JDBC 的脚本初始化数据库失败

Initialize a database with a script using Spring JDBC fails

我正在尝试使用遵循 75.3 Initialize a database using Spring JDBC.

的视图和规则来初始化我的数据库

75.3 Initialize a database using Spring JDBC

Spring JDBC has a DataSource initializer feature. Spring Boot enables it by default and loads SQL from the standard locations schema.sql and data.sql (in the root of the classpath). In addition Spring Boot will load the schema-${platform}.sql and data-${platform}.sql files (if present), where platform is the value of spring.datasource.platform, e.g. you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql etc.). Spring Boot enables the fail-fast feature of the Spring JDBC initializer by default, so if the scripts cause exceptions the application will fail to start. The script locations can be changed by setting spring.datasource.schema and spring.datasource.data, and neither location will be processed if spring.datasource.initialize=false.

这部分说如果我放一个 schema-postgresql.sql 它应该用文件包含的脚本初始化我的数据库。

不幸的是,脚本以以下错误结束

Caused by: org.postgresql.util.PSQLException: syntax error at end of input SQL state Position: 169 at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2310) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2023) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:217) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:421) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:310) ~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:473) ~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE] ... 64 common frames omitted

但是,如果我 运行 这个来自 pgAdminIII 的脚本没有错误,并且创建具有相应规则的视图没有任何问题。

我做错了什么?

这是我的 Spring 重现它的引导示例的结构。

src/main/java/com/example/model/Person.java

package com.example.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Person implements Serializable {

    private static final long serialVersionUID = 1334414548362400146L;

    @Id
    private long id;

    @Column(nullable = false, length = 100)
    private String name = "";

    @Column(nullable = false, length = 100)
    private String surname = "";

}

src/main/java/com/example/model/PersonRole.java

package com.example.model;

import java.io.Serializable;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

@Entity
public class PersonRole implements Serializable {
    private static final long serialVersionUID = -3953147119216643027L;

    @EmbeddedId
     private PersonRoleKey primaryKey;
}

src/main/java/com/example/model/PersonRoleKey.java

package com.example.model;

import java.io.Serializable;

import javax.persistence.Embeddable;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.ForeignKey;
import javax.persistence.ManyToOne;
import javax.persistence.PrimaryKeyJoinColumn;

@Embeddable
public class PersonRoleKey implements Serializable {

    private static final long serialVersionUID = 2105526364632711640L;

    @ManyToOne(optional = false)
    @PrimaryKeyJoinColumn(foreignKey = @ForeignKey(name = "person_fk"))
    private Person person;

    @Enumerated(EnumType.STRING)
    private Role role;

}

src/main/java/com/example/model/Role.java

package com.example.model;

public enum Role {
    ADMIN, USER;
}

src/main/java/com/example/DemoApplication.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

src/main/resources/application.属性

#Database configuration
spring.datasource.url: jdbc:postgresql://localhost:5432/postgres
spring.datasource.driverClassName: org.postgresql.Driver
spring.datasource.username: postgres
spring.datasource.password: postgres
spring.datasource.platform: postgresql
spring.datasource.continue-on-error: false

spring.jpa.properties.hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql: true
spring.jpa.generate-ddl: true
spring.jpa.hibernate.ddl-auto: update
#default means org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
spring.jpa.properties.hibernate.implicit_naming_strategy: default
spring.jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
#spring.jpa.properties.hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
spring.jpa.properties.hibernate.auto_quote_keyword: true
spring.jpa.show-sql: false

src/main/resources/schema-postgresql.sql

CREATE OR REPLACE VIEW v_peoples_roles AS
 SELECT p.id,
    p.name,
    p.surname,
    pr.role
   FROM (person p
     JOIN personrole pr ON ((p.id = pr.person_id)));

CREATE OR REPLACE RULE insert_v_peoples_roles AS
    ON INSERT TO v_peoples_roles DO INSTEAD ( INSERT INTO person (id, name, surname)
  VALUES (new.id, new.name, new.surname);
 INSERT INTO personrole (person_id, role)
  VALUES (new.id, new.role);
);

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <hibernate.version>5.2.2.Final</hibernate.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

整个代码可以从 here 下载。

编辑

poz's comment 的基础上,我将 schema-postgresql.sql 更改为

CREATE OR REPLACE VIEW v_peoples_roles AS
 SELECT p.id,
    p.name,
    p.surname,
    pr.role
   FROM (person p
     JOIN personrole pr ON ((p.id = pr.person_id)));

CREATE OR REPLACE FUNCTION insert_into_v_people_roles() RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO person (id, name, surname) VALUES (new.id, new.name, new.surname);
  INSERT INTO personrole (person_id, role) VALUES (new.id, new.role);
  RETURN new;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS insert_v_peoples_roles ON v_peoples_roles;
CREATE TRIGGER insert_v_peoples_roles INSTEAD OF INSERT ON v_peoples_roles FOR EACH ROW EXECUTE PROCEDURE insert_into_v_people_roles();

但它产生了另一个错误,与预测的完全一样。

ERROR: unterminated quoted string at or near "$$ BEGIN INSERT INTO person (id, name, surname) VALUES (new.id, new.name, new.surname)"

因为pozs没有贴出自己的答案,时间过去了所以自己做。

CREATE RULE 更改为 INSTEAD OF 触发并将 $$-quoting 更改为 '-quoting 解决了问题。唯一的问题是我必须转义函数定义中的所有撇号。不过并没有那么痛。