Spring启动没有执行schema.sql脚本

时间:2015-01-13 12:34:20

标签: java mysql jdbc spring-boot

我正在开发一个Spring Boot Web应用程序,如果尚未创建MySql数据库,则需要创建它。所以我已经完成了当前数据库的转储,以便有一个空模式。将它放在 / src / main / resources 中,因此maven在构建war文件时将其带到 / WEB-INF / classes 。这就是我的 application.properties 的配置方式(根据Spring docs,应该从脚本中创建数据库):

# DataSource settings: set here configurations for the database connection
spring.datasource.url = jdbc:mysql://localhost:3306/working_zones
spring.datasource.username = root
spring.datasource.password = password
spring.datasource.driverClassName = com.mysql.jdbc.Driver

# Specify the DBMS
spring.jpa.database = MYSQL

# Hibernate settings are prefixed with spring.jpa.hibernate.*
spring.jpa.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

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

2015-01-13 13:30:24.334 [main] ERROR o.s.boot.SpringApplication - Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.dataSource; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$NonEmbeddedConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceInitializer': Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.UncategorizedScriptException: Failed to execute database script; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown database 'working_zones'
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:301) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1186) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:370) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1095) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:990) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) ~[spring-beans-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:975) ~[spring-context-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:752) ~[spring-context-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) ~[spring-context-4.0.8.RELEASE.jar:4.0.8.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109) ~[spring-boot-1.1.9.RELEASE.jar:1.1.9.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691) [spring-boot-1.1.9.RELEASE.jar:1.1.9.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:320) [spring-boot-1.1.9.RELEASE.jar:1.1.9.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:952) [spring-boot-1.1.9.RELEASE.jar:1.1.9.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:941) [spring-boot-1.1.9.RELEASE.jar:1.1.9.RELEASE]

所以看起来Spring试图连接到数据库甚至schema.sql,其中包含创建数据库的脚本,而不是执行。 Stack Overflow中有一些相关的问题,但即使我尝试使用spring.datasource.initialize=true,仍然无法使其正常工作......

3 个答案:

答案 0 :(得分:3)

好吧,看起来您无法通过常见的JDBC连接执行此操作:creating a database in mysql from java

因此,Spring Boot无法自动为您执行此操作。

请不要将数据库创建与其创建内容混合在一起:表格,程序,触发器等。

<强>更新

是的,您可以在应用程序启动时执行此操作。您只需要一个单独的初始化程序,它在dataSourceInitializer之前有一个订单。

答案 1 :(得分:0)

应用程序启动时的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);
        }
    }
}

以下是DatabaseProperties。

@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());
    }
}

侦听器应该由META-INF / spring.factories中的config激活。

org.springframework.context.ApplicationListener=\
yourpackage.DatabaseCreationListener

如果您想在底层IDE中使用配置语法提示,请添加可选的depenedncy 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"
    }
  ]
}

答案 2 :(得分:0)

this github 帖子中所述,您可以添加: spring.datasource.url=jdbc:mysql://localhost:3309/course_api_db?createDatabaseIfNotExist=true

执行schema.sql