Java Spring引导未执行schema.sql脚本

Java Spring引导未执行schema.sql脚本,java,mysql,jdbc,spring-boot,Java,Mysql,Jdbc,Spring Boot,我正在开发一个SpringBootWeb应用程序,如果还没有创建MySql数据库,我想创建它。因此,我对当前数据库进行了转储,以获得它的空模式。将它放在/src/main/resources中,以便maven在构建war文件时将其带到/WEB-INF/classes中。这就是我的application.properties的配置方式(根据,应该从脚本创建DB): 这就是我在尝试运行应用程序时遇到的错误(它抱怨不存在DB): 因此,Spring似乎甚至试图连接到数据库schema.sql,该数据库

我正在开发一个SpringBootWeb应用程序,如果还没有创建MySql数据库,我想创建它。因此,我对当前数据库进行了转储,以获得它的空模式。将它放在/src/main/resources中,以便maven在构建war文件时将其带到/WEB-INF/classes中。这就是我的application.properties的配置方式(根据,应该从脚本创建DB):

这就是我在尝试运行应用程序时遇到的错误(它抱怨不存在DB):


因此,Spring似乎甚至试图连接到数据库
schema.sql
,该数据库包含创建DB的脚本,但未执行。堆栈溢出中有一些相关的问题,但仍然无法使其工作,即使我尝试使用
spring.datasource.initialize=true

好吧,看起来您无法通过普通JDBC连接实现这一点:

因此,SpringBoot不能自动为您做到这一点

请不要将数据库的创建与其内容(表、过程、触发器等)的创建混为一谈

更新


是的,您可以在应用程序启动时执行此操作。您只需要一个单独的初始值设定项,该初始值设定项在应用程序启动时的listerner可以解决该问题。下面是代码

public class DatabaseCreationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

    private AtomicBoolean received = new AtomicBoolean(false);

    private ConfigurableEnvironment environment;

    private Pattern JDBC_URL_PATTERN = Pattern.compile("jdbc:([a-zA-Z0-9_]+)://[0-9.:]+(?:/([a-zA-Z0-9_]+))?(\\?.*)?");

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        // Think about twice invoking this listener
        if (!received.compareAndSet(false, true)) {
            return;
        }

        environment = event.getEnvironment();

        // ConditionalOnClass
        ClassLoader classLoader = event.getSpringApplication().getClassLoader();
        if (!isPresent("org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType", classLoader)) {
            return;
        }

        // DatabaseProperties
        val databaseProperties = bind(DatabaseProperties.PREFIX, new DatabaseProperties());
        if (!databaseProperties.shouldCreate()) {
            return;
        }

        // DataSourceProperties
        val dataSourceProperties = bind(databaseProperties.getDatasourceConfigPrefix(), new DataSourceProperties());

        // Check for connection url
        String url = dataSourceProperties.getUrl();
        if (url == null) return;
        Matcher matcher = JDBC_URL_PATTERN.matcher(url);
        if (!matcher.matches()) return;

        // Extract database provider and schema name from connection url
        String databaseProvider = matcher.group(1);
        String schemaName = matcher.group(2);
        if (isBlank(schemaName)) return;

        // Reset connection url
        dataSourceProperties.setUrl(url.replace("/" + schemaName, ""));

        // Build a new datasource and do create schema
        DataSource dataSource = buildDataSource(dataSourceProperties);
        try (Connection connection = DataSourceUtils.getConnection(dataSource)) {
            connection.createStatement().execute(createSchemaIfAbsent(databaseProvider, schemaName));
        } catch (SQLException ignored) {
        }
    }

    private <T> T bind(String prefix, T t) {
        RelaxedDataBinder binder = new RelaxedDataBinder(t, prefix);
        binder.bind(new PropertySourcesPropertyValues(environment.getPropertySources()));
        return t;
    }

    private static DataSource buildDataSource(DataSourceProperties dataSourceProperties) {
        String url = dataSourceProperties.getUrl();
        String username = dataSourceProperties.getUsername();
        String password = dataSourceProperties.getPassword();
        return new SingleConnectionDataSource(url, username, password, false);
    }

    private static String createSchemaIfAbsent(String databaseProvider, String schemaName) {
        DatabaseDialects dialects = DatabaseDialects.getDatabaseDialect(databaseProvider);
        if (dialects == null) {
            throw new IllegalArgumentException("Unknown schema:" + schemaName);
        }
        switch (dialects) {
            case MYSQL:
                return "CREATE DATABASE IF NOT EXISTS " + schemaName;
            default:
                throw new UnsupportedOperationException("Unsupported schema:" + dialects);
        }
    }
}
侦听器应该通过META-INF/spring.factories中的配置激活

org.springframework.context.ApplicationListener=\
yourpackage.DatabaseCreationListener
如果要在底层IDE中使用配置语法提示,请添加可选的Dependency
spring boot configuration processor
和文件META-INF/additional-spring-configuration-metadata.json

{
  "groups": [
    {
      "sourceType": "yourpackage.DatabaseProperties",
      "name": "spring.database",
      "type": "yourpackage.DatabaseProperties"
    }
  ],
  "properties": [
    {
      "sourceType": "yourpackage.DatabaseProperties",
      "defaultValue": false,
      "name": "auto-create",
      "type": "java.lang.Boolean"
    },
    {
      "sourceType": "youpackage.DatabaseProperties",
      "defaultValue": "spring.datasource",
      "name": "datasource-config-prefix",
      "type": "java.lang.String"
    }
  ]
}

未知数据库“工作区”
对你说了什么吗?@ArtemBilan,
schema.sql
脚本具有
创建数据库(如果不存在“工作区”
命令)。它没有被执行,因此程序在开始建立JDBC连接时找不到DB。这就是问题所在…你需要数据库在那里,甚至可以连接到它。故障表明您的数据源无法连接到数据库,这是正确的,因为它不存在。@M.Deinum那么我是否至少需要创建数据库,并依靠脚本来创建表/关系?当应用程序启动时,没有其他方法来执行整个作业吗?我不知道,数据库的名称在URL中,所以它需要在前面。另见@ArtemBilan的答案。好吧,这就是我想要的,谢谢。我从一开始就在寻找DB创建本身,不知道这个问题是否会引导你走另一条路。
@Data
@ConfigurationProperties(prefix = DatabaseProperties.PREFIX)
public class DatabaseProperties {

    public final static String PREFIX = "spring.database";

    private boolean autoCreate;

    private String datasourceConfigPrefix = "spring.datasource";

    public boolean shouldCreate() {
        return isAutoCreate() && isNotBlank(getDatasourceConfigPrefix());
    }
}
org.springframework.context.ApplicationListener=\
yourpackage.DatabaseCreationListener
{
  "groups": [
    {
      "sourceType": "yourpackage.DatabaseProperties",
      "name": "spring.database",
      "type": "yourpackage.DatabaseProperties"
    }
  ],
  "properties": [
    {
      "sourceType": "yourpackage.DatabaseProperties",
      "defaultValue": false,
      "name": "auto-create",
      "type": "java.lang.Boolean"
    },
    {
      "sourceType": "youpackage.DatabaseProperties",
      "defaultValue": "spring.datasource",
      "name": "datasource-config-prefix",
      "type": "java.lang.String"
    }
  ]
}