Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class HibernateDatastoreConnectionSourcesRegistrar implements BeanDefinitionRegi
boolean shouldConfigureDataSourceBean = GrailsVersion.isAtLeastMajorMinor(3, 3)
String dataSourceBeanName = isDefault ? Settings.SETTING_DATASOURCE : "${Settings.SETTING_DATASOURCE}_$dataSourceName"

if (!registry.containsBeanDefinition(dataSourceBeanName) && shouldConfigureDataSourceBean) {
// A data source registered by other means, also as a singleton, is the one to keep
if (!registry.isBeanNameInUse(dataSourceBeanName) && shouldConfigureDataSourceBean) {
def dataSourceBean = new RootBeanDefinition()
dataSourceBean.setTargetType(DataSource)
dataSourceBean.setBeanClass(InstanceFactoryBean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ package org.grails.orm.hibernate.support
import grails.gorm.tests.HibernateGormDatastoreSpec
import org.grails.datastore.gorm.bootstrap.support.InstanceFactoryBean
import org.grails.datastore.mapping.config.Settings
import org.grails.datastore.mapping.core.connections.ConnectionSource
import org.hibernate.SessionFactory
import org.springframework.beans.factory.support.DefaultListableBeanFactory
import org.springframework.beans.factory.support.RootBeanDefinition
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.Unroll

import javax.sql.DataSource

Expand Down Expand Up @@ -72,4 +76,28 @@ class HibernateDatastoreConnectionSourcesRegistrarSpec extends HibernateGormData
!registry.containsBeanDefinition("sessionFactory_dataSource")
!registry.containsBeanDefinition("transactionManager_dataSource")
}

@Unroll
def "test postProcessBeanDefinitionRegistry keeps a #registeredAs data source bean that is already registered"() {
given:
def registry = new DefaultListableBeanFactory()
def dataSource = new DriverManagerDataSource('jdbc:h2:mem:registeredDataSource')
register(registry, dataSource)
def registrar = new HibernateDatastoreConnectionSourcesRegistrar([ConnectionSource.DEFAULT])

when:
registrar.postProcessBeanDefinitionRegistry(registry)

then:
registry.getBean(Settings.SETTING_DATASOURCE, DataSource).is(dataSource)

where:
registeredAs | register
'definition' | { DefaultListableBeanFactory beanFactory, DataSource instance ->
beanFactory.registerBeanDefinition(Settings.SETTING_DATASOURCE, new RootBeanDefinition(DataSource, { instance }))
}
'singleton' | { DefaultListableBeanFactory beanFactory, DataSource instance ->
beanFactory.registerSingleton(Settings.SETTING_DATASOURCE, instance)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {

String defaultDataSourceBeanName = ConnectionSource.DEFAULT
String defaultSessionFactoryBeanName = SESSION_FACTORY_BEAN_NAME
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
Set<String> dataSources = new LinkedHashSet<String>([defaultDataSourceBeanName])
boolean enableReload = false
boolean grailsPlugin = false

Expand Down Expand Up @@ -85,21 +85,15 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
@CompileStatic
void configureDataSources(PropertyResolver config) {

Set<String> dataSourceNames = new HashSet<String>()
// The datastore always creates the default connection source, whether or not it is configured
Set<String> dataSourceNames = new LinkedHashSet<String>([ConnectionSource.DEFAULT])

if (config == null) {
dataSourceNames = [defaultDataSourceBeanName] as Set
}
else {
if (config != null) {
Map dataSources = config.getProperty(DATA_SOURCES, Map, Collections.emptyMap())

if (dataSources != null && !dataSources.isEmpty()) {
dataSourceNames.addAll(AbstractConnectionSources.toValidConnectionSourceNames(dataSources))
}
Map dataSource = (Map) config.getProperty(DEFAULT_DATA_SOURCE_NAME, Map, Collections.emptyMap())
if (dataSource != null && !dataSource.isEmpty()) {
dataSourceNames.add(ConnectionSource.DEFAULT)
}
}
this.dataSources = dataSourceNames
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@
*/
package grails.orm.bootstrap

import javax.sql.DataSource

import grails.gorm.annotation.Entity
import org.grails.datastore.mapping.core.connections.ConnectionSource
import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.hibernate.dialect.H2Dialect
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.Specification
import spock.lang.Unroll

/**
* Created by graemerocher on 29/01/14.
Expand Down Expand Up @@ -90,6 +97,79 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
}

}

@Unroll
void "the default connection source is the first of the data sources when #configured"() {
when: 'an initializer is created'
def datastoreInitializer = new HibernateDatastoreSpringInitializer(config, Person)

then: 'the default connection source comes first, followed by the additional data sources in configuration order'
datastoreInitializer.dataSources instanceof LinkedHashSet
datastoreInitializer.dataSources as List == expected

where:
configured | config || expected
'nothing is configured' | [:] || [ConnectionSource.DEFAULT]
'only dataSource is configured' | ['dataSource.url': 'jdbc:h2:mem:orderDefault'] || [ConnectionSource.DEFAULT]
'only dataSources are configured' | ['dataSources.books.url': 'jdbc:h2:mem:books',
'dataSources.moreBooks.url': 'jdbc:h2:mem:moreBooks'] || [ConnectionSource.DEFAULT, 'books', 'moreBooks']
'both are configured' | ['dataSources.books.url': 'jdbc:h2:mem:books',
'dataSource.url': 'jdbc:h2:mem:orderDefault',
'dataSources.moreBooks.url': 'jdbc:h2:mem:moreBooks'] || [ConnectionSource.DEFAULT, 'books', 'moreBooks']
}

void "the default data source is registered as the dataSource bean when no dataSource is configured"() {
given: 'an initializer without dataSource configuration'
def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)

when: 'the application is configured'
def applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
def hibernateDatastore = applicationContext.getBean(HibernateDatastore)

then: 'the data source GORM uses is available by name and by type'
applicationContext.getBeansOfType(DataSource).keySet() == ['dataSource'] as Set
applicationContext.getBean('dataSource', DataSource).is(hibernateDatastore.dataSource)
applicationContext.getBean('dataSource', DataSource).connection.withCloseable { it.metaData.URL } ==
'jdbc:h2:mem:grailsDB'

cleanup:
applicationContext?.close()
}

void "the default data source is registered as the dataSource bean when only additional data sources are configured"() {
given: 'an initializer configuring only an additional data source'
def datastoreInitializer = new HibernateDatastoreSpringInitializer([
'dataSources.books.url': 'jdbc:h2:mem:defaultDataSourceBeanBooks;LOCK_TIMEOUT=10000'
], Person)

when: 'the application is configured'
def applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()

then: 'both the default and the additional data source are beans'
applicationContext.getBeansOfType(DataSource).keySet() == ['dataSource', 'dataSource_books'] as Set
applicationContext.getBean('dataSource', DataSource).connection.withCloseable { it.metaData.URL } ==
'jdbc:h2:mem:grailsDB'
applicationContext.getBean('dataSource_books', DataSource).connection.withCloseable { it.metaData.URL } ==
'jdbc:h2:mem:defaultDataSourceBeanBooks'

cleanup:
applicationContext?.close()
}

void "the data source passed to configureForDataSource remains the dataSource bean"() {
given: 'a data source and an initializer without dataSource configuration'
def dataSource = new DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000')
def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)

when: 'the application is configured for that data source'
def applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configureForDataSource(dataSource)

then: 'the dataSource bean is the given data source'
applicationContext.getBean('dataSource', DataSource).is(dataSource)

cleanup:
applicationContext?.close()
}
}
@Entity
class Person {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class HibernateDatastoreConnectionSourcesRegistrar implements BeanDefinitionRegi
boolean shouldConfigureDataSourceBean = GrailsVersion.isAtLeastMajorMinor(3, 3)
String dataSourceBeanName = isDefault ? Settings.SETTING_DATASOURCE : "${Settings.SETTING_DATASOURCE}_$dataSourceName"

if (!registry.containsBeanDefinition(dataSourceBeanName) && shouldConfigureDataSourceBean) {
// A data source registered by other means, also as a singleton, is the one to keep
if (!registry.isBeanNameInUse(dataSourceBeanName) && shouldConfigureDataSourceBean) {
def dataSourceBean = new RootBeanDefinition()
dataSourceBean.setTargetType(ResolvableType.forClassWithGenerics(InstanceFactoryBean, DataSource))
dataSourceBean.setBeanClass(InstanceFactoryBean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import org.grails.datastore.mapping.config.Settings
import org.grails.datastore.mapping.core.connections.ConnectionSource
import org.hibernate.SessionFactory
import org.springframework.beans.factory.support.DefaultListableBeanFactory
import org.springframework.beans.factory.support.RootBeanDefinition
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.Unroll

import javax.sql.DataSource

Expand Down Expand Up @@ -85,4 +88,28 @@ class HibernateDatastoreConnectionSourcesRegistrarSpec extends HibernateGormData
!registry.containsBeanDefinition("sessionFactory_dataSource")
!registry.containsBeanDefinition("transactionManager_dataSource")
}

@Unroll
def "test postProcessBeanDefinitionRegistry keeps a #registeredAs data source bean that is already registered"() {
given:
def registry = new DefaultListableBeanFactory()
def dataSource = new DriverManagerDataSource('jdbc:h2:mem:registeredDataSource')
register(registry, dataSource)
def registrar = new HibernateDatastoreConnectionSourcesRegistrar([ConnectionSource.DEFAULT])

when:
registrar.postProcessBeanDefinitionRegistry(registry)

then:
registry.getBean(Settings.SETTING_DATASOURCE, DataSource).is(dataSource)

where:
registeredAs | register
'definition' | { DefaultListableBeanFactory beanFactory, DataSource instance ->
beanFactory.registerBeanDefinition(Settings.SETTING_DATASOURCE, new RootBeanDefinition(DataSource, { instance }))
}
'singleton' | { DefaultListableBeanFactory beanFactory, DataSource instance ->
beanFactory.registerSingleton(Settings.SETTING_DATASOURCE, instance)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {

String defaultDataSourceBeanName = ConnectionSource.DEFAULT
String defaultSessionFactoryBeanName = SESSION_FACTORY_BEAN_NAME
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
Set<String> dataSources = new LinkedHashSet<String>([defaultDataSourceBeanName])
boolean enableReload = false
boolean grailsPlugin = false
Closure beanDefinitions
Expand Down Expand Up @@ -89,21 +89,15 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
@CompileStatic
void configureDataSources(PropertyResolver config) {

Set<String> dataSourceNames = new HashSet<String>()
// The datastore always creates the default connection source, whether or not it is configured
Set<String> dataSourceNames = new LinkedHashSet<String>([ConnectionSource.DEFAULT])

if (config == null) {
dataSourceNames = [defaultDataSourceBeanName] as Set
}
else {
if (config != null) {
Map dataSources = config.getProperty(DATA_SOURCES, Map, Collections.emptyMap())

if (dataSources != null && !dataSources.isEmpty()) {
dataSourceNames.addAll(AbstractConnectionSources.toValidConnectionSourceNames(dataSources))
}
Map dataSource = (Map) config.getProperty(DEFAULT_DATA_SOURCE_NAME, Map, Collections.emptyMap())
if (dataSource != null && !dataSource.isEmpty()) {
dataSourceNames.add(ConnectionSource.DEFAULT)
}
}
this.dataSources = dataSourceNames
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@
*/
package grails.orm.bootstrap

import javax.sql.DataSource

import grails.gorm.annotation.Entity
import org.grails.datastore.mapping.core.connections.ConnectionSource
import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.hibernate.dialect.H2Dialect
import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.AutoCleanup
import spock.lang.Specification
import spock.lang.Unroll

/**
* Created by graemerocher on 29/01/14.
Expand Down Expand Up @@ -93,6 +98,70 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
return true
}
}

@Unroll
void "the default connection source is the first of the data sources when #configured"() {
when: 'an initializer is created'
def datastoreInitializer = new HibernateDatastoreSpringInitializer(config, Person)

then: 'the default connection source comes first, followed by the additional data sources in configuration order'
datastoreInitializer.dataSources instanceof LinkedHashSet
datastoreInitializer.dataSources as List == expected

where:
configured | config || expected
'nothing is configured' | [:] || [ConnectionSource.DEFAULT]
'only dataSource is configured' | ['dataSource.url': 'jdbc:h2:mem:orderDefault'] || [ConnectionSource.DEFAULT]
'only dataSources are configured' | ['dataSources.books.url': 'jdbc:h2:mem:books',
'dataSources.moreBooks.url': 'jdbc:h2:mem:moreBooks'] || [ConnectionSource.DEFAULT, 'books', 'moreBooks']
'both are configured' | ['dataSources.books.url': 'jdbc:h2:mem:books',
'dataSource.url': 'jdbc:h2:mem:orderDefault',
'dataSources.moreBooks.url': 'jdbc:h2:mem:moreBooks'] || [ConnectionSource.DEFAULT, 'books', 'moreBooks']
}

void "the default data source is registered as the dataSource bean when no dataSource is configured"() {
given: 'an initializer without dataSource configuration'
def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)

when: 'the application is configured'
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
def hibernateDatastore = applicationContext.getBean(HibernateDatastore)

then: 'the data source GORM uses is available by name and by type'
applicationContext.getBeansOfType(DataSource).keySet() == ['dataSource'] as Set
applicationContext.getBean('dataSource', DataSource).is(hibernateDatastore.dataSource)
applicationContext.getBean('dataSource', DataSource).connection.withCloseable { it.metaData.URL } ==
'jdbc:h2:mem:grailsDB'
}

void "the default data source is registered as the dataSource bean when only additional data sources are configured"() {
given: 'an initializer configuring only an additional data source'
def datastoreInitializer = new HibernateDatastoreSpringInitializer([
'dataSources.books.url': 'jdbc:h2:mem:defaultDataSourceBeanBooks;LOCK_TIMEOUT=10000'
], Person)

when: 'the application is configured'
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()

then: 'both the default and the additional data source are beans'
applicationContext.getBeansOfType(DataSource).keySet() == ['dataSource', 'dataSource_books'] as Set
applicationContext.getBean('dataSource', DataSource).connection.withCloseable { it.metaData.URL } ==
'jdbc:h2:mem:grailsDB'
applicationContext.getBean('dataSource_books', DataSource).connection.withCloseable { it.metaData.URL } ==
'jdbc:h2:mem:defaultDataSourceBeanBooks'
}

void "the data source passed to configureForDataSource remains the dataSource bean"() {
given: 'a data source and an initializer without dataSource configuration'
def dataSource = new DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000')
def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)

when: 'the application is configured for that data source'
applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configureForDataSource(dataSource)

then: 'the dataSource bean is the given data source'
applicationContext.getBean('dataSource', DataSource).is(dataSource)
}
}
@Entity
class Person {
Expand Down
11 changes: 11 additions & 0 deletions grails-doc/src/en/guide/conf/dataSource.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ dataSource {
}
----

When GORM for Hibernate is applied, it always creates a default data source, and that data source is available as the `dataSource` bean, to inject by name or by type, even when the application has no `dataSource` block. Without a `dataSource` block, it connects to the in-memory H2 database `jdbc:h2:mem:grailsDB`, which requires the H2 driver on the classpath.

To run without this default data source, exclude GORM for Hibernate by its plugin name, `hibernate`, with the `grails.plugin.excludes` setting. None of its beans, such as `hibernateDatastore`, `sessionFactory` and `transactionManager`, are registered then, and the application has a `dataSource` bean only when it has a `dataSource` block:

[source,yaml]
----
grails:
plugin:
excludes: hibernate
----

WARNING: When configuring the DataSource do not include the type or the def keyword before any of the configuration settings as Groovy will treat these as local variable definitions and they will not be processed. For example the following is invalid:

[source,groovy]
Expand Down
Loading