Spring 启动和数据库初始化工作不正常
Spring Boot and Database Initialization not working properly
schema.sql 中的脚本已执行,但 data.sql 中的脚本未执行,
不确定我错过了什么?
我正在使用Spring使用两个数据源启动我的数据库配置如下
@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
我设法实例化了 2 个数据源,并使用 this test project 在其中一个中启动了模式和数据。希望对您有所帮助,也许我错过了您的一些要求,使我的建议无效:(
供参考(我猜你已经看到了):https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-two-datasources
当我们看到 org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer class 的内容通过 *.sql files.It 负责数据库初始化时,问题出在数据源初始化上 post 构造方法如下
@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 脚本中提供该模式,那么这些方法将创建模式,但我想在 schema 为 created/updated 之后执行操作,因为 class 包含
@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;
}
}
如果在休眠模式 creation/updation 操作之后我们有 spring 启动默认 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
事件
对于那些在 SpringBoot 2.1+ 世界中偶然发现这个问题的人。
首先,我认为是重要 class 的完整 class 名称(“publishEvent”的对象...“org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent”
对于未来的读者,这个 class 似乎在这两个版本之间消失了:
以下确实存在:
以下已不存在:
这是我如何“编码”种子数据(“data.sql”)..当我有一个Java-Config heavy class.
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 引导魔法。特别是 spring 启动 2.5.
schema.sql 中的脚本已执行,但 data.sql 中的脚本未执行, 不确定我错过了什么?
我正在使用Spring使用两个数据源启动我的数据库配置如下
@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
我设法实例化了 2 个数据源,并使用 this test project 在其中一个中启动了模式和数据。希望对您有所帮助,也许我错过了您的一些要求,使我的建议无效:(
供参考(我猜你已经看到了):https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-two-datasources
当我们看到 org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer class 的内容通过 *.sql files.It 负责数据库初始化时,问题出在数据源初始化上 post 构造方法如下
@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 脚本中提供该模式,那么这些方法将创建模式,但我想在 schema 为 created/updated 之后执行操作,因为 class 包含
@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;
}
}
如果在休眠模式 creation/updation 操作之后我们有 spring 启动默认 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
事件
对于那些在 SpringBoot 2.1+ 世界中偶然发现这个问题的人。
首先,我认为是重要 class 的完整 class 名称(“publishEvent”的对象...“org.springframework.boot.autoconfigure.jdbc.DataSourceInitializedEvent”
对于未来的读者,这个 class 似乎在这两个版本之间消失了:
以下确实存在:
以下已不存在:
这是我如何“编码”种子数据(“data.sql”)..当我有一个Java-Config heavy class.
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 引导魔法。特别是 spring 启动 2.5.