Spring 启动 - 多数据源 - hbm2ddl 问题
Spring Boot - multiple datasource - hbm2ddl issue
我在 Spring Boot(版本 2.1.3.Final)应用程序中定义了多个数据源,对于其中一个,我将 hbm2ddl 设置为更新。但是,如果我第二次 运行 应用程序,它总是尝试执行 CREATE TABLE 语句(而不是 alter,或者在实体没有变化的情况下没有语句)
数据源定义如下所示:
// @formatter:off
@EnableJpaRepositories(entityManagerFactoryRef = "triggerEMF", transactionManagerRef = "triggerTM", basePackages = {
"com.customer.trigger.repository" }, excludeFilters = @Filter(CDHRepository.class))
// @formatter:on
@Configuration
@EnableTransactionManagement
public class TriggerDSConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(TriggerDSConfig.class);
@Autowired
private Environment env;
@Primary
@Bean(name = "triggerDS")
@ConfigurationProperties("trigger.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "triggerTM")
public PlatformTransactionManager psqlTransactionManager(@Qualifier("triggerEMF") EntityManagerFactory customerEntityManagerFactory) {
return new JpaTransactionManager(customerEntityManagerFactory);
}
@Primary
@Bean(name = "triggerEMF")
public LocalContainerEntityManagerFactoryBean psqlEntityManager(EntityManagerFactoryBuilder builder) throws SQLException {
Map<String, String> props = new HashMap<String, String>();
props.put("hibernate.hbm2ddl.auto", env.getProperty("trigger.hbm2ddl"));
props.put("hibernate.dialect", env.getProperty("trigger.dialect"));
props.put("hibernate.default_schema", "public");
DataSource ds = dataSource();
ds.getConnection(); // eager connection pool init
return builder.dataSource(ds) //
.packages("com.customer.trigger.model.entity") //
.persistenceUnit("trigger-pu") //
.properties(props) //
.build(); //
}
// Initialise & Populate DS in Local profile
@Bean
@Profile({ "local" })
public DataSourceInitializer triggerEngineDBInit(@Qualifier("triggerDS") DataSource datasource) {
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
resourceDatabasePopulator.addScript(new ClassPathResource("db/schema-triggerengine.sql"));
resourceDatabasePopulator.addScript(new ClassPathResource("db/data-triggerengine.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(datasource);
dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
dataSourceInitializer.setEnabled(env.getProperty("trigger.datasource.initialize", Boolean.class, false));
return dataSourceInitializer;
}
}
此数据源的 YAML 定义如下所示:
trigger:
datasource:
jdbcUrl: jdbc:h2:~/triggerdb2;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
username: sa
password: sa
hbm2ddl: update
dialect: org.hibernate.dialect.H2Dialect
我可以确认没有执行 DDL 脚本。这是失败的 hbm2ddl。我对它进行了相当深入的调试,目前已经做到了 class:
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaMigratorImpl.java
table 信息为空 - 因此休眠生成 create table 语句(而不是 alter or no 语句)。但是当然 table 存在,所以失败并出现以下错误:
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigrator.java:559)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigrator.java:504)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.createTable(AbstractSchemaMigrator.java:277)
at org.hibernate.tool.schema.internal.GroupedSchemaMigratorImpl.performTablesMigration(GroupedSchemaMigratorImpl.java:71)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:207)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:114)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:183)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1821)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1758)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=13=](AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.customer.trigger.Application.main(Application.java:11)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:558)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.h2.jdbc.JdbcSQLException: Table "SUGGESTION_VOD__C" already exists; SQL statement:
PostgreSQL 也会出现此问题(不仅仅是 H2)
有什么想法吗?
复制者:
我读到了你的问题。问题不取决于数据库,我认为你的问题是你没有为你的项目使用数据库版本控制。
版本控制为您处理所有这些事情(它知道何时创建或更改等等)。
这些是您可以使用的很好的数据库版本控制
liquibase
flyway
我的意见是使用 liquibase,因为它有很好的文档,更容易
我运行你的项目在我的本地。当我 运行 两次收到 table 已经存在的错误时,你是对的。解决方案是
Map<String, String> props = new HashMap<String, String>();
props.put("hibernate.hbm2ddl.auto", "update");
props.put("hibernate.dialect", env.getProperty("trigger.dialect"));
props.put("hibernate.default_schema", env.getProperty("trigger.schema"));
您的 yml 中可能缺少一些 属性。还对这些属性使用@Value。
删除以下 yaml 配置后:
# temp:
# use_jdbc_metadata_defaults: "false"
问题不再出现。当然,现在我们收到了奇怪的 CLOB 相关错误,但它们似乎没有真正的影响,所以这是一个单独的问题。
我在 Spring Boot(版本 2.1.3.Final)应用程序中定义了多个数据源,对于其中一个,我将 hbm2ddl 设置为更新。但是,如果我第二次 运行 应用程序,它总是尝试执行 CREATE TABLE 语句(而不是 alter,或者在实体没有变化的情况下没有语句)
数据源定义如下所示:
// @formatter:off
@EnableJpaRepositories(entityManagerFactoryRef = "triggerEMF", transactionManagerRef = "triggerTM", basePackages = {
"com.customer.trigger.repository" }, excludeFilters = @Filter(CDHRepository.class))
// @formatter:on
@Configuration
@EnableTransactionManagement
public class TriggerDSConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(TriggerDSConfig.class);
@Autowired
private Environment env;
@Primary
@Bean(name = "triggerDS")
@ConfigurationProperties("trigger.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "triggerTM")
public PlatformTransactionManager psqlTransactionManager(@Qualifier("triggerEMF") EntityManagerFactory customerEntityManagerFactory) {
return new JpaTransactionManager(customerEntityManagerFactory);
}
@Primary
@Bean(name = "triggerEMF")
public LocalContainerEntityManagerFactoryBean psqlEntityManager(EntityManagerFactoryBuilder builder) throws SQLException {
Map<String, String> props = new HashMap<String, String>();
props.put("hibernate.hbm2ddl.auto", env.getProperty("trigger.hbm2ddl"));
props.put("hibernate.dialect", env.getProperty("trigger.dialect"));
props.put("hibernate.default_schema", "public");
DataSource ds = dataSource();
ds.getConnection(); // eager connection pool init
return builder.dataSource(ds) //
.packages("com.customer.trigger.model.entity") //
.persistenceUnit("trigger-pu") //
.properties(props) //
.build(); //
}
// Initialise & Populate DS in Local profile
@Bean
@Profile({ "local" })
public DataSourceInitializer triggerEngineDBInit(@Qualifier("triggerDS") DataSource datasource) {
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
resourceDatabasePopulator.addScript(new ClassPathResource("db/schema-triggerengine.sql"));
resourceDatabasePopulator.addScript(new ClassPathResource("db/data-triggerengine.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(datasource);
dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
dataSourceInitializer.setEnabled(env.getProperty("trigger.datasource.initialize", Boolean.class, false));
return dataSourceInitializer;
}
}
此数据源的 YAML 定义如下所示:
trigger:
datasource:
jdbcUrl: jdbc:h2:~/triggerdb2;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
username: sa
password: sa
hbm2ddl: update
dialect: org.hibernate.dialect.H2Dialect
我可以确认没有执行 DDL 脚本。这是失败的 hbm2ddl。我对它进行了相当深入的调试,目前已经做到了 class: https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/GroupedSchemaMigratorImpl.java
table 信息为空 - 因此休眠生成 create table 语句(而不是 alter or no 语句)。但是当然 table 存在,所以失败并出现以下错误:
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigrator.java:559)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigrator.java:504)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.createTable(AbstractSchemaMigrator.java:277)
at org.hibernate.tool.schema.internal.GroupedSchemaMigratorImpl.performTablesMigration(GroupedSchemaMigratorImpl.java:71)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:207)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:114)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:183)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1821)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1758)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=13=](AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.customer.trigger.Application.main(Application.java:11)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:558)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.h2.jdbc.JdbcSQLException: Table "SUGGESTION_VOD__C" already exists; SQL statement:
PostgreSQL 也会出现此问题(不仅仅是 H2)
有什么想法吗?
复制者:
我读到了你的问题。问题不取决于数据库,我认为你的问题是你没有为你的项目使用数据库版本控制。 版本控制为您处理所有这些事情(它知道何时创建或更改等等)。
这些是您可以使用的很好的数据库版本控制 liquibase flyway
我的意见是使用 liquibase,因为它有很好的文档,更容易
我运行你的项目在我的本地。当我 运行 两次收到 table 已经存在的错误时,你是对的。解决方案是
Map<String, String> props = new HashMap<String, String>();
props.put("hibernate.hbm2ddl.auto", "update");
props.put("hibernate.dialect", env.getProperty("trigger.dialect"));
props.put("hibernate.default_schema", env.getProperty("trigger.schema"));
您的 yml 中可能缺少一些 属性。还对这些属性使用@Value。
删除以下 yaml 配置后:
# temp:
# use_jdbc_metadata_defaults: "false"
问题不再出现。当然,现在我们收到了奇怪的 CLOB 相关错误,但它们似乎没有真正的影响,所以这是一个单独的问题。