Skip to content

really hard to initialize second datasource with HibernateGormAutoConfiguration #14660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
benheilers opened this issue Jan 22, 2015 · 4 comments

Comments

@benheilers
Copy link

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:

  1. 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

  1. 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)
  1. 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.

  1. 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 grails/grails-gsp-spring-boot#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.

@benheilers
Copy link
Author

I also thought HibernateDatastoreSpringInitializer#configureForDataSource() was a really cool method, until I saw that is is hard-coded to register the "dataSource" bean:

        applicationContext.beanFactory.registerSingleton(defaultDataSourceBeanName, dataSource)

@benheilers
Copy link
Author

What appears to be another bug in HibernateDatastoreSpringInitializer:

            if(!beanDefinitionRegistry.containsBeanDefinition("entityInterceptor")) {
                entityInterceptor(EmptyInterceptor)
            }
...
                        entityInterceptor = ref("entityInterceptor$suffix")

So I get an exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityInterceptor_dataSource_vertica' is defined

If it's intended that we define the beans ourselves, then please document this somewhere.

Thanks!

@benheilers
Copy link
Author

And there's also this code:

                if (!beanDefinitionRegistry.containsBeanDefinition("transactionManager")) {
                    "transactionManager$suffix"(GrailsHibernateTransactionManager) { bean ->
                        bean.autowire = "byName"
                        sessionFactory = ref(sessionFactoryName)
                        dataSource = ref(dataSourceName)
                    }
                }

I had to initialize the HibernateDatastoreSpringInitializer with the datasources in the opposite order to make sure that "transactionManager_dataSource_vertica" is registered before "transactionManager":

        //NOTE: change from parent class, add vertica data source, force ordering of vertica datasource first
        initializer.dataSources = [ 'dataSource_vertica', 'dataSource'  ] as LinkedHashSet<String>

@benheilers
Copy link
Author

HibernatePluginSupport made sure to add these properties to the session factory:

                dataSourceName = datasourceName
                sessionFactoryBeanName = "sessionFactory$suffix"

Without them I end up with two ConfigurableLocalSessionFactoryBeans which both have field "dataSourceName" set to "DEFAULT" even though the second bean has field "dataSource" set to the vertica data source. This of course causes problems in building the session factory proxy.

Am I doing something wrong?

@jdaugherty jdaugherty transferred this issue from grails/grails-gsp-spring-boot Mar 10, 2025
@jdaugherty jdaugherty transferred this issue from apache/grails-data-mapping Apr 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants