Hi, I couldn't find the issue tracker for gorm-hibernate4-spring-boot, so please help me find it if this one is GSP-specific.
I wanted to set up a second data source (primary data source against postgres and second datasource against Vertica). We have the same code in a Grails project that works fine.
Here are the issues I have found so far:
- HibernateGormAutoConfiguration is hardcoded as:
initializer = new HibernateDatastoreSpringInitializer(classLoader, packages as String[])
initializer.resourceLoader = resourceLoader
initializer.setConfiguration(getDatastoreConfiguration())
initializer.configureForBeanDefinitionRegistry(registry)
But HibernateDatastoreSpringInitializer is hardcoded as:
String defaultDataSourceBeanName = "dataSource"
Set<String> dataSources = [defaultDataSourceBeanName]
So adding a second datasource actually required:
a) subclassing HibernateGormAutoConfiguration with my own auto configuration class that copies/pastes the code except to insert the line:
//NOTE: change from parent class, add vertica data source
initializer.dataSources = [ 'dataSource', 'dataSource_vertica' ]
b) add HibernateGormAutoConfiguration to the excludes list of auto configuration, or else both run
- HibernateGormAutoConfiguration.EagerInitProcessor has the code:
But the PostInitializingHandling bean is registered once per datasource, we can see this in a for loop over datasources:
"org.grails.gorm.hibernate.internal.POST_INIT_BEAN-${dataSourceName}$suffix"(PostInitializationHandling) { bean ->
grailsApplication = ref(GrailsApplication.APPLICATION_ID)
bean.lazyInit = false
}
So we end up with an exception:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [grails.orm.bootstrap.HibernateDatastoreSpringInitializer$PostInitializationHandling] is defined: expected single matching bean but found 2: org.grails.gorm.hibernate.internal.POST_INIT_BEAN-dataSource_vertica_dataSource_vertica,org.grails.gorm.hibernate.internal.POST_INIT_BEAN-dataSource
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:365)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:968)
at org.grails.datastore.gorm.boot.autoconfigure.HibernateGormAutoConfiguration$EagerInitProcessor.postProcessBeforeInitialization(HibernateGormAutoConfiguration.groovy:115)
- The datasource names are a little wonky. In HibernateDatastoreSpringInitializer there is code like this:
for(dataSourceName in dataSources) {
boolean isDefault = dataSourceName == defaultDataSourceBeanName
String suffix = isDefault ? '' : '_' + dataSourceName
String prefix = isDefault ? '' : dataSourceName + '_'
def sessionFactoryName = isDefault ? defaultSessionFactoryBeanName : "sessionFactory$suffix"
def hibConfig = configurationObject["hibernate$suffix"] ?: configurationObject["hibernate"]
...
dataSource = ref(dataSourceName)
For my second data source I desire the ultimate data source's name to be "dataSource_vertica". So that's the name I've put into the HibernateDatastoreSpringInitializer.dataSources field.
But you can see above the suffix has been determined as _dataSource_vertica, so now the session factory has been named "sessionFactory_dataSource_vertica" instead of just "sessionFactory_vertica". And it's looking for hibernate properties "hibernate_dataSource_vertica".
So I think the "suffix" and "prefix" variables should not include dataSourceName.
- In my application.yml I have:
hibernate:
hbm2ddl.auto: ''
dialect: mycompany.hibernate.dialect.PostgresSequencePerTableDialect
hibernate_dataSource_vertica:
hbm2ddl.auto: ''
dialect: mycompany.hibernate.dialect.VerticaDialect
But as I showed in #1, HibernateGormAutoConfiguration has:
initializer.setConfiguration(getDatastoreConfiguration())
where getDatastoreConfiguration() is defined as:
protected Properties getDatastoreConfiguration() {
if(environment != null) {
def config = environment.getSubProperties("hibernate.")
def properties = new Properties()
for(entry in config.entrySet()) {
properties.put("hibernate.${entry.key}".toString(), entry.value)
}
return properties
}
}
You see the same data store configuration is used for both datasources, whereas I wanted to use a different one for each.