【问题标题】:Spring Boot and Database Initialization not working properlySpring Boot 和数据库初始化无法正常工作
【发布时间】:2017-09-14 10:24:49
【问题描述】:

schema.sql 中的脚本被执行,但 data.sql 中的脚本没有执行, 不确定我错过了什么?

我使用Spring Boot和两个数据源我的数据库配置如下

@PropertySource({ "classpath:application.properties" })
@Configuration
@EnableJpaRepositories(
    basePackages = "com.projectx.mysql", 
    entityManagerFactoryRef = "userEntityManager", 
    transactionManagerRef = "userTransactionManager"
)
public class DataBaseConfig {

    @Autowired
    Environment env;


    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean userEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(primaryDataSource());
        em.setPackagesToScan(new String[] { "com.projectx.mysql" });
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<String, Object>();
        properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.hibernate.ddl-auto_mysql"));
        properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect_mysql"));
        properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }


    @Primary
    @Bean
    public PlatformTransactionManager userTransactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(userEntityManager().getObject());
        return transactionManager;
    }
}

和.properties文件配置如下

spring.datasource.initialize=true
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.show-sql: true
spring.jpa.hibernate.ddl-auto_mysql=update
spring.jpa.properties.hibernate.dialect_mysql=org.hibernate.dialect.MySQL5Dialect

【问题讨论】:

  • 你能把你执行sql脚本的代码贴出来吗?第二个数据源如何处理?
  • 其他数据连接到 mongodb 服务器。问题是,如果我将其指定为 schema.sql,则内容会被执行,但如果我将相同的内容放在 data.sql 中,它不会执行
  • 什么是data.sql和schema.sql?
  • 尝试显式设置spring.datasource.data=data.sql

标签: spring-boot sql-scripts


【解决方案1】:

对于那些在 SpringBoot 2.1+ 世界中偶然发现这个问题的人。

首先,我认为是重要类的完整类名(“publishEvent”的对象...“org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent”

对于未来的读者来说,这个类似乎已经在这两个版本之间消失了:

以下确实存在:

https://docs.spring.io/spring-boot/docs/2.0.0.M3/api/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializedEvent.html

以下不再存在:

https://docs.spring.io/spring-boot/docs/2.1.0.M1/api/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializedEvent.html

这是我“编码”种子数据(“data.sql”)的方式......当我有一个 Java-Config 重类时。

import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;

@Bean   
public DataSource getDataSource() {
    //not shown 
}


@Bean
public DataSourceInitializer dataSourceInitializer(DataSource ds) {
    ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
    resourceDatabasePopulator.addScript(new ClassPathResource("/data.sql"));

    DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
    dataSourceInitializer.setDataSource(ds);
    dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
    return dataSourceInitializer;
}

调试提示。您可能希望使用不是魔术文件名的文件名(“data.sql”是一个魔术名称)来故意避免 Spring Boot 魔术。特别是从 Spring Boot 2.5 开始。

【讨论】:

  • 不错的解决方案!我被困了一段时间。
【解决方案2】:

当我们看到org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer 类的内容通过 *.sql 文件处理数据库初始化时,问题出在数据源初始化。它具有如下构造方法

    @PostConstruct
    public void init() {
        if (!this.properties.isInitialize()) {
            logger.debug("Initialization disabled (not running DDL scripts)");
            return;
        }
        if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
                false).length > 0) {
            this.dataSource = this.applicationContext.getBean(DataSource.class);
        }
        if (this.dataSource == null) {
            logger.debug("No DataSource found so not initializing");
            return;
        }
        runSchemaScripts();
    } 

runSchemaScripts() 方法将在休眠模式创建和更新操作执行之前初始化数据,因此如果未生成数据库模式,那么如果您在 SQL 脚本中提供,这些方法将创建模式,但我想在之后执行操作架构已创建/更新,因为该类包含

    @Override
    public void onApplicationEvent(DataSourceInitializedEvent event) {
        if (!this.properties.isInitialize()) {
            logger.debug("Initialization disabled (not running data scripts)");
            return;
        }
        // NOTE the event can happen more than once and
        // the event datasource is not used here
        if (!this.initialized) {
            runDataScripts();
            this.initialized = true;
        }
    }

如果在休眠模式创建/更新操作之后当我们有弹簧靴默认Datasource 创建机制时调用。 但是由于我自己创建Datasource,所以它没有创建DataSourceInitializedEvent,所以数据初始化脚本data.sql没有被执行。 所以我改变了我的数据源创建逻辑来创建DataSourceInitializedEvent,如下所示,这解决了我的问题。

    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean userEntityManager() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(primaryDataSource());
        em.setPackagesToScan(new String[] { "com.projectx.mysql" });
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<String, Object>();
        properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.hibernate.ddl-auto_mysql"));
        properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect_mysql"));
        properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
        em.setJpaPropertyMap(properties);

        this.applicationContext.publishEvent(new DataSourceInitializedEvent(primaryDataSource()));

        return em;
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

添加了this.applicationContext.publishEvent(new DataSourceInitializedEvent(primaryDataSource())); 以创建DataSourceInitializedEvent 事件

【讨论】:

【解决方案3】:

我设法用this test project 实例化了 2 个数据源并在其中一个中启动模式和数据。希望对您有所帮助,也许我错过了您的一些要求,使我的建议无效:(

对于参考(猜你已经看到了):https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-two-datasources

【讨论】:

  • 问题不在于两个连接正常。但是数据初始化没有完成。但是架构脚本运行正常
  • 您是否尝试过设置 ddl-auto=none。 “如果你想在 JPA 应用程序(使用 Hibernate)中使用 schema.sql 初始化,那么如果 Hibernate 尝试创建相同的表,ddl-auto=create-drop 将导致错误。为了避免这些错误,将 ddl-auto 显式设置为“”(首选)或“无”。无论您是否使用 ddl-auto=create-drop,您始终可以使用 data.sql 来初始化新数据。”
猜你喜欢
  • 1970-01-01
  • 2022-01-18
  • 2017-10-16
  • 1970-01-01
  • 1970-01-01
  • 2018-02-14
  • 2021-11-02
  • 2020-02-09
  • 2019-01-23
相关资源
最近更新 更多