Java 使用SpringJDBC脚本初始化数据库失败

Java 使用SpringJDBC脚本初始化数据库失败,java,postgresql,jdbc,spring-boot,sql-scripts,Java,Postgresql,Jdbc,Spring Boot,Sql Scripts,我正试图用一个视图和一条规则来初始化我的数据库 75.3使用Spring JDBC初始化数据库 SpringJDBC具有数据源初始值设定项功能。Spring Boot默认启用它,并从标准位置schema.SQL和data.SQL(在类路径的根目录中)加载SQL。此外,Spring Boot将加载schema-${platform}.sql和data-${platform}.sql文件(如果存在),其中platform是Spring.datasource.platform的值,例如,您可以选择将其

我正试图用一个视图和一条规则来初始化我的数据库

75.3使用Spring JDBC初始化数据库

SpringJDBC具有数据源初始值设定项功能。Spring Boot默认启用它,并从标准位置schema.SQL和data.SQL(在类路径的根目录中)加载SQL。此外,Spring Boot将加载schema-${platform}.sql和data-${platform}.sql文件(如果存在),其中platform是Spring.datasource.platform的值,例如,您可以选择将其设置为数据库的供应商名称(hsqldb、h2、oracle、mysql、postgresql等)。SpringBoot默认启用SpringJDBC初始值设定项的FailFast特性,因此如果脚本导致异常,应用程序将无法启动。可以通过设置spring.datasource.schema和spring.datasource.data来更改脚本位置,如果spring.datasource.initialize=false,则不会处理这两个位置

本节说明,如果我放置一个
模式postgresql.sql
,它应该使用文件包含的脚本初始化数据库

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

原因:org.postgresql.util.psqleexception:输入SQL状态结束时出现语法错误 职位:169 在org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2310)~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] 在org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2023)~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] 在org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:217)~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] 在org.postgresql.jdbc.PgStatement.execute(PgStatement.java:421)~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] 在org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318)~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] 在org.postgresql.jdbc.PgStatement.execute(PgStatement.java:310)~[postgresql-9.4.1209.jre7.jar:9.4.1209.jre7] 在org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:473)~[spring-jdbc-4.3.2.RELEASE.jar:4.3.2.RELEASE] ... 省略64个公共帧

但是,如果从
pgAdminIII
运行此脚本,则不会出现任何错误,并且创建具有相应规则的视图时不会出现任何问题

我做错了什么

这是我的Spring Boot示例的结构来重现它

src/main/java/com/example/model/Person.java src/main/java/com/example/model/PersonRole.java src/main/java/com/example/model/PersonRoleKey.java src/main/java/com/example/model/Role.java src/main/java/com/example/DemoApplication.java src/main/resources/application.properties src/main/resources/schema-postgresql.sql pom.xml 但它产生了另一个错误,与预测完全一样

错误:“$$BEGIN INSERT INTO person(id、名称、姓氏)值(new.id、new.name、new.姓氏)”处或附近未终止的引号字符串

因为他还没有发布自己的答案,而且一段时间过去了,我正在自己做

创建规则
更改为
而不是
触发,并将
$$
-quoting更改为
-quoting解决了问题。唯一的问题是我必须避开函数定义中的所有撇号。不过也没那么痛苦。

因为他还没有发布自己的答案,而且一段时间过去了,我自己在做


创建规则
更改为
而不是
触发,并将
$$
-quoting更改为
-quoting解决了问题。唯一的问题是我必须避开函数定义中的所有撇号。不过也没那么痛苦。

我的猜测是,PostgreSQL的
创建规则
语句的非标准语法会混淆JDBC和/或Spring(它们可能希望拆分
上的语句,以便只逐个执行它们)。如果是这种情况,那么它只会尝试执行
CREATE RULE
语句的一半,这可能会导致出现错误。如果您使用最新版本的PostgreSQL,您可以尝试使用
而不是
触发器(不使用美元报价)而不是规则(使用美元报价,可能会出现相同的问题)。您好,感谢您的提示。但是,如果不实际使用双美元报价,似乎不可能创建
而不是
触发器,因为需要它来定义在
执行过程
部分触发器之后使用的函数。这不只是spring boot中的一个bug吗?我应该能够使用这种机制执行任何本机数据库脚本,因为它应该做这样的事情。我错了吗?你可以用单引号创建函数,比如
create FUNCTION。。。正如“body”
所见——美元报价只是为了方便起见(在一个单引号常量中使用单引号常量会更困难)——对于您的问题:不,spring只是在引擎盖下使用JDBC,它设计为一次只执行一条语句(其他用途有批处理功能)。出于这个原因,许多基于JDBC的框架使用了令人讨厌的变通方法。。。。。。f、 前。liquibase的
endDelimiter
,其上有
splitStatements
。不幸的是,我不熟悉Spring boot,所以我不是100%确定。将双美元报价改为单美元报价解决了这个问题。如果你总结你的评论并将其作为答案发布,我将接受并投票表决。我学到了很多,谢谢!我的猜测可能是,PostgreSQL的
创建规则
语句的非标准语法混淆了JDBC和/或Spring(它们可能希望拆分
上的语句,以便只逐个执行它们)。如果是这种情况,那么它只会尝试执行
CREATE RULE
语句的一半,这可能会导致出现错误。
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 = "";

}
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;
}
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;

}
package com.example.model;

public enum Role {
    ADMIN, USER;
}
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);
    }
}
#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
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);
);
<?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>
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();