You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/08/13 22:33:24 UTC
(grails-core) branch test/abstract-datastore-initializer created (now eee486fbc0)
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a change to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
at eee486fbc0 Fix enableReload being silently ignored; remove dead grailsPlugin property
This branch includes the following new commits:
new f5d0465dac Add unit coverage for AbstractDatastoreInitializer and clean up IDE warnings
new 42612d3755 Close test coverage gaps in the Hibernate5/7 and MongoDB datastore initializers
new 6236d0cbff Fix H5 HibernateDatastoreSpringInitializer warnings and add PropertyResolver constructor coverage
new dfa6f23ec0 Remove now-dead SESSION_FACTORY_BEAN_NAME constant from H5 initializer
new de92bf1342 Remove dead getTestDbUrl/defaultSessionFactoryBeanName/SESSION_FACTORY_BEAN_NAME from H7 initializer
new 393464309e Fix setMongoBeanName being silently ignored when building a new MongoClient
new 4d44c4a60b Fix defaultDataSourceBeanName being hardcoded around instead of used
new eec3be04f0 Fix setDatabaseName being silently ignored on MongoDbDataStoreSpringInitializer
new ca684d4712 Remove dead registerApplicationIfNotPresent property
new eee486fbc0 Fix enableReload being silently ignored; remove dead grailsPlugin property
The 10 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(grails-core) 08/10: Fix setDatabaseName being silently ignored on MongoDbDataStoreSpringInitializer
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit eec3be04f04af61beb7b2ed2678f871fd9ed29bd
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 16:07:40 2026 -0500
Fix setDatabaseName being silently ignored on MongoDbDataStoreSpringInitializer
MongodbGrailsPlugin sets initializer.databaseName = applicationName to
default the Mongo database name to the Grails app's name, but that field
was never read anywhere - every Grails+MongoDB app not explicitly setting
grails.mongodb.databaseName silently got the wrong database.
Adds applyDatabaseNameFallback(), called at the top of getBeanDefinitions()
(the only method MongodbGrailsPlugin actually invokes - it doesn't go
through configure()), which injects databaseName as a
grails.mongodb.databaseName fallback property on `configuration` only when
it was customized away from the class default and the configuration
doesn't already specify one explicitly. Handles both configuration shapes
this class accepts: ConfigurableEnvironment (the default and the
Map-constructor path, both mutable via propertySources) and a generic Map
(covering Grails' own Config, which this module can't depend on directly
since it's designed to work standalone outside Grails - tested here via a
minimal Map+PropertyResolver double instead).
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../MongoDbDataStoreSpringInitializer.groovy | 23 ++++
.../MongoDbDataStoreSpringInitializerSpec.groovy | 39 +++++++
...ongoDbDataStoreSpringInitializerUnitSpec.groovy | 122 +++++++++++++++++++++
3 files changed, 184 insertions(+)
diff --git a/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy b/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy
index 7672e0fe8e..98f43a5e90 100644
--- a/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy
+++ b/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy
@@ -25,6 +25,8 @@ import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.support.GenericApplicationContext
+import org.springframework.core.env.ConfigurableEnvironment
+import org.springframework.core.env.MapPropertySource
import org.springframework.util.ClassUtils
import grails.mongodb.MongoEntity
@@ -36,6 +38,7 @@ import org.grails.datastore.gorm.support.AbstractDatastorePersistenceContextInte
import org.grails.datastore.gorm.support.DatastorePersistenceContextInterceptor
import org.grails.datastore.mapping.config.DatastoreServiceMethodInvokingFactoryBean
import org.grails.datastore.mapping.mongo.MongoDatastore
+import org.grails.datastore.mapping.mongo.config.MongoSettings
import org.grails.datastore.mapping.mongo.connections.MongoConnectionSourceFactory
/**
@@ -83,8 +86,28 @@ class MongoDbDataStoreSpringInitializer extends AbstractDatastoreInitializer {
return applicationContext
}
+ /**
+ * Applies {@link #databaseName} as a {@code grails.mongodb.databaseName} fallback on
+ * {@link #configuration} when it was customized via {@link #setDatabaseName(String)} and the
+ * configuration does not already specify a database name explicitly.
+ */
+ protected void applyDatabaseNameFallback() {
+ if (databaseName == DEFAULT_DATABASE_NAME || configuration.containsProperty(MongoSettings.SETTING_DATABASE_NAME)) {
+ return
+ }
+ if (configuration instanceof ConfigurableEnvironment) {
+ ((ConfigurableEnvironment) configuration).propertySources.addFirst(
+ new MapPropertySource('mongoDbDataStoreSpringInitializer.databaseName', [(MongoSettings.SETTING_DATABASE_NAME): databaseName])
+ )
+ }
+ else if (configuration instanceof Map) {
+ ((Map) configuration).put(MongoSettings.SETTING_DATABASE_NAME, databaseName)
+ }
+ }
+
@Override
Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) {
+ applyDatabaseNameFallback()
return {
def callable = getCommonConfiguration(beanDefinitionRegistry, 'mongo')
callable.delegate = delegate
diff --git a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
index 300d447ab7..5b33af0e38 100644
--- a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
+++ b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
@@ -61,6 +61,45 @@ class MongoDbDataStoreSpringInitializerSpec extends AutoStartedMongoSpec {
mongoDatastore.destroy()
}
+ void "Test setDatabaseName is honored as a fallback when no explicit grails.mongodb.databaseName is configured"() {
+ given: "an initializer with a customized database name and no explicit database name config"
+ def initializer = makeInitializer([
+ (MongoSettings.SETTING_HOST): mongoHost,
+ (MongoSettings.SETTING_PORT): mongoPort,
+ ], Person)
+ initializer.setDatabaseName('fallbackDb')
+
+ when: "the initializer is configured"
+ def applicationContext = initializer.configure()
+ def mongoDatastore = applicationContext.getBean(MongoDatastore)
+
+ then: "the fallback database name is used"
+ mongoDatastore.getDefaultDatabase() == 'fallbackDb'
+
+ cleanup:
+ mongoDatastore.destroy()
+ }
+
+ void "Test setDatabaseName is ignored when grails.mongodb.databaseName is explicitly configured"() {
+ given: "an initializer with both a customized database name and an explicit database name config"
+ def initializer = makeInitializer([
+ (MongoSettings.SETTING_DATABASE_NAME): 'explicit',
+ (MongoSettings.SETTING_HOST) : mongoHost,
+ (MongoSettings.SETTING_PORT) : mongoPort,
+ ], Person)
+ initializer.setDatabaseName('fallbackDb')
+
+ when: "the initializer is configured"
+ def applicationContext = initializer.configure()
+ def mongoDatastore = applicationContext.getBean(MongoDatastore)
+
+ then: "the explicit configuration wins over the fallback"
+ mongoDatastore.getDefaultDatabase() == 'explicit'
+
+ cleanup:
+ mongoDatastore.destroy()
+ }
+
void "Test that MongoDbDatastoreSpringInitializer can setup GORM for MongoDB from scratch"() {
when: "the initializer used to setup GORM for MongoDB"
def initializer = makeInitializer([
diff --git a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
index c708ac5c4e..3a8c694ec3 100644
--- a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
+++ b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
@@ -21,6 +21,8 @@ package grails.mongodb.bootstrap
import com.mongodb.MongoClientSettings
import com.mongodb.client.MongoClient
import grails.mongodb.MongoEntity
+import org.grails.datastore.mapping.mongo.config.MongoSettings
+import org.springframework.core.env.PropertyResolver
import spock.lang.Specification
/**
@@ -109,6 +111,126 @@ class MongoDbDataStoreSpringInitializerUnitSpec extends Specification {
then:
initializer.mongo.is(client)
}
+
+ void 'applyDatabaseNameFallback does nothing when the database name was never customized'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+
+ expect:
+ !initializer.configuration.containsProperty(MongoSettings.SETTING_DATABASE_NAME)
+
+ when:
+ initializer.applyDatabaseNameFallback()
+
+ then:
+ !initializer.configuration.containsProperty(MongoSettings.SETTING_DATABASE_NAME)
+ }
+
+ void 'applyDatabaseNameFallback injects the customized database name into a ConfigurableEnvironment when not already set'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+ initializer.setDatabaseName('customDb')
+
+ when:
+ initializer.applyDatabaseNameFallback()
+
+ then:
+ initializer.configuration.getProperty(MongoSettings.SETTING_DATABASE_NAME) == 'customDb'
+ }
+
+ void 'applyDatabaseNameFallback does not override an already-configured database name on a ConfigurableEnvironment'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+ initializer.configuration.propertySources.addFirst(
+ new org.springframework.core.env.MapPropertySource('test', [(MongoSettings.SETTING_DATABASE_NAME): 'explicit']))
+ initializer.setDatabaseName('customDb')
+
+ when:
+ initializer.applyDatabaseNameFallback()
+
+ then:
+ initializer.configuration.getProperty(MongoSettings.SETTING_DATABASE_NAME) == 'explicit'
+ }
+
+ void 'applyDatabaseNameFallback injects the customized database name into a Map-based PropertyResolver when not already set'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+ def config = new MapPropertyResolver()
+ initializer.configuration = config
+ initializer.setDatabaseName('customDb')
+
+ when:
+ initializer.applyDatabaseNameFallback()
+
+ then:
+ config.get(MongoSettings.SETTING_DATABASE_NAME) == 'customDb'
+ }
+
+ void 'applyDatabaseNameFallback does not override an already-configured database name on a Map-based PropertyResolver'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+ def config = new MapPropertyResolver((MongoSettings.SETTING_DATABASE_NAME): 'explicit')
+ initializer.configuration = config
+ initializer.setDatabaseName('customDb')
+
+ when:
+ initializer.applyDatabaseNameFallback()
+
+ then:
+ config.get(MongoSettings.SETTING_DATABASE_NAME) == 'explicit'
+ }
+}
+
+/**
+ * Minimal {@link PropertyResolver} backed directly by a {@link Map}, standing in for
+ * Grails' own {@code Config} type (which is both a {@code Map} and a {@code PropertyResolver})
+ * without requiring a dependency on grails-core, which this module deliberately excludes.
+ */
+class MapPropertyResolver extends LinkedHashMap<String, Object> implements PropertyResolver {
+
+ boolean containsProperty(String key) {
+ containsKey(key)
+ }
+
+ @Override
+ String getProperty(String key) {
+ get(key) as String
+ }
+
+ @Override
+ String getProperty(String key, String defaultValue) {
+ containsKey(key) ? get(key) as String : defaultValue
+ }
+
+ @Override
+ def <T> T getProperty(String key, Class<T> targetType) {
+ get(key) as T
+ }
+
+ @Override
+ def <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
+ containsKey(key) ? get(key) as T : defaultValue
+ }
+
+ @Override
+ String getRequiredProperty(String key) {
+ get(key) as String
+ }
+
+ @Override
+ def <T> T getRequiredProperty(String key, Class<T> targetType) {
+ get(key) as T
+ }
+
+ @Override
+ String resolvePlaceholders(String text) {
+ text
+ }
+
+ @Override
+ String resolveRequiredPlaceholders(String text) {
+ text
+ }
}
class MappedThing implements MongoEntity<MappedThing> {
(grails-core) 05/10: Remove dead getTestDbUrl/defaultSessionFactoryBeanName/SESSION_FACTORY_BEAN_NAME from H7 initializer
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit de92bf134205cf1af87e9a6fe19f8b9368940769
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 15:05:47 2026 -0500
Remove dead getTestDbUrl/defaultSessionFactoryBeanName/SESSION_FACTORY_BEAN_NAME from H7 initializer
Mirrors the H5 cleanup: getTestDbUrl() was never invoked anywhere, and
defaultSessionFactoryBeanName's value was never read by the sessionFactory
bean registration it appeared to configure (which uses a hardcoded
literal). Removing both leaves SESSION_FACTORY_BEAN_NAME with no
remaining consumer, so it goes too.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy | 6 ------
1 file changed, 6 deletions(-)
diff --git a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
index 58940a847f..327729e083 100644
--- a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
@@ -49,13 +49,11 @@ import org.grails.orm.hibernate.support.HibernateDatastoreConnectionSourcesRegis
*/
class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
- public static final String SESSION_FACTORY_BEAN_NAME = 'sessionFactory'
public static final String DEFAULT_DATA_SOURCE_NAME = Settings.SETTING_DATASOURCE
public static final String DATA_SOURCES = Settings.SETTING_DATASOURCES
public static final String TEST_DB_URL = 'jdbc:h2:mem:grailsDb;LOCK_TIMEOUT=10000;DB_CLOSE_DELAY=-1'
String defaultDataSourceBeanName = ConnectionSource.DEFAULT
- String defaultSessionFactoryBeanName = SESSION_FACTORY_BEAN_NAME
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
boolean enableReload = false
boolean grailsPlugin = false
@@ -139,10 +137,6 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
}
}
- protected String getTestDbUrl() {
- TEST_DB_URL
- }
-
@CompileStatic
ApplicationContext configureForDataSource(DataSource dataSource) {
GenericApplicationContext applicationContext = createApplicationContext()
(grails-core) 04/10: Remove now-dead SESSION_FACTORY_BEAN_NAME constant from H5 initializer
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit dfa6f23ec0790f7a5a2ff962aac50167426e8b94
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 15:03:53 2026 -0500
Remove now-dead SESSION_FACTORY_BEAN_NAME constant from H5 initializer
Its only consumer, defaultSessionFactoryBeanName, was removed in the
prior commit for being unwired/no-op, leaving this constant unused too;
the actual sessionFactory bean registration already uses a hardcoded
literal rather than this constant.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy | 1 -
1 file changed, 1 deletion(-)
diff --git a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
index b5ee5ab4f2..0f36a69057 100644
--- a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
@@ -47,7 +47,6 @@ import org.grails.orm.hibernate.support.HibernateDatastoreConnectionSourcesRegis
*/
class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
- public static final String SESSION_FACTORY_BEAN_NAME = 'sessionFactory'
public static final String DEFAULT_DATA_SOURCE_NAME = Settings.SETTING_DATASOURCE
public static final String DATA_SOURCES = Settings.SETTING_DATASOURCES
public static final String TEST_DB_URL = 'jdbc:h2:mem:grailsDb;LOCK_TIMEOUT=10000;DB_CLOSE_DELAY=-1'
(grails-core) 02/10: Close test coverage gaps in the Hibernate5/7 and MongoDB datastore initializers
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 42612d37550bc748580b06cd073380329408f559
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 14:36:47 2026 -0500
Close test coverage gaps in the Hibernate5/7 and MongoDB datastore initializers
Adds tests for branches of the three production AbstractDatastoreInitializer
subclasses that were previously unexercised: the OSIV interceptor
registration branch and configureForDataSource(DataSource) for both
Hibernate5 and Hibernate7, Hibernate7's IllegalStateException guard when
hibernateDatastore fails to register, and the Map/Collection<Class>
constructor form for both.
For MongoDB, covers the mongo != null branch (reusing a pre-existing
MongoClient) in both configure() and getBeanDefinitions(), the
package-scanning constructor, and adds a new Docker-free
MongoDbDataStoreSpringInitializerUnitSpec covering the isMappedClass/
collectMappedClasses mixed-entity filtering and the deprecated setters
that the Docker-backed spec never reaches.
grails-data-neo4j was excluded: it is not part of the root build, its
README states it hasn't been updated for the current release, and its
standalone build is broken, so no tests could be written or verified
there.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../HibernateDatastoreSpringInitializerSpec.groovy | 65 +++++++++++
.../HibernateDatastoreSpringInitializerSpec.groovy | 80 ++++++++++++++
.../MongoDbDataStoreSpringInitializerSpec.groovy | 46 ++++++++
...ongoDbDataStoreSpringInitializerUnitSpec.groovy | 120 +++++++++++++++++++++
4 files changed, 311 insertions(+)
diff --git a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index 88fb4c628d..f2e542589b 100644
--- a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -19,17 +19,28 @@
package grails.orm.bootstrap
import grails.gorm.annotation.Entity
+import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.hibernate.dialect.H2Dialect
+import org.springframework.beans.factory.support.RootBeanDefinition
+import org.springframework.context.ConfigurableApplicationContext
+import org.springframework.context.support.GenericApplicationContext
+import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.transaction.PlatformTransactionManager
+import spock.lang.AutoCleanup
import spock.lang.Specification
+import javax.sql.DataSource
+
/**
* Created by graemerocher on 29/01/14.
*/
class HibernateDatastoreSpringInitializerSpec extends Specification{
+ @AutoCleanup
+ ConfigurableApplicationContext applicationContext
+
void "Test configure multiple data sources"() {
given:"An initializer instance"
Map config = [
@@ -90,6 +101,60 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
}
}
+
+ void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
+ given: "An initializer built from a Collection of persistent classes"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(
+ ['dataSource.url': 'jdbc:h2:mem:collectionCtor;LOCK_TIMEOUT=10000', 'hibernate.hbm2ddl.auto': 'create'],
+ [Person] as Collection<Class>)
+
+ when: "the application is configured"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+
+ then: "GORM is bootstrapped with the given entity"
+ applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
+ Person.withNewSession { Person.count() == 0 }
+ }
+
+ void "Test configureForDataSource bootstraps GORM around a pre-existing DataSource"() {
+ given: "a DataSource created ahead of time"
+ def dataSource = new DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000', 'sa', '')
+ dataSource.driverClassName = 'org.h2.Driver'
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['hibernate.hbm2ddl.auto': 'create'], Person)
+
+ when: "the initializer is configured around that DataSource"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configureForDataSource(dataSource)
+
+ then: "the pre-existing DataSource is registered and reused rather than a new one being built"
+ applicationContext.getBean(HibernateDatastoreSpringInitializer.DEFAULT_DATA_SOURCE_NAME, DataSource).is(dataSource)
+ Person.withNewSession { Person.count() == 0 }
+ }
+
+ void "Test the OSIV interceptor is registered when the registry is a web application"() {
+ given: "a registry that signals it belongs to a web application"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['dataSource.url': 'jdbc:h2:mem:osivEnabled;LOCK_TIMEOUT=10000'], Person)
+ def registry = new GenericApplicationContext()
+ registry.registerBeanDefinition('grailsControllerHelper', new RootBeanDefinition(Object))
+
+ when:
+ datastoreInitializer.configureForBeanDefinitionRegistry(registry)
+ registry.refresh()
+ applicationContext = registry
+
+ then:
+ registry.containsBean('openSessionInViewInterceptor')
+ }
+
+ void "Test the OSIV interceptor is not registered for a non-web application registry"() {
+ given: "An initializer instance configured against a plain, non-web registry"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['dataSource.url': 'jdbc:h2:mem:osivDisabled;LOCK_TIMEOUT=10000'], Person)
+
+ when:
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+
+ then:
+ !applicationContext.containsBean('openSessionInViewInterceptor')
+ }
}
@Entity
class Person {
diff --git a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index 9549d17d68..afa580120f 100644
--- a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -23,11 +23,17 @@ import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.SessionFactory
import org.hibernate.dialect.H2Dialect
+import org.springframework.beans.factory.support.BeanDefinitionRegistry
+import org.springframework.beans.factory.support.RootBeanDefinition
import org.springframework.context.ConfigurableApplicationContext
+import org.springframework.context.support.GenericApplicationContext
+import org.springframework.jdbc.datasource.DriverManagerDataSource
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.AutoCleanup
import spock.lang.Specification
+import javax.sql.DataSource
+
/**
* Created by graemerocher on 29/01/14.
*/
@@ -93,6 +99,80 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
return true
}
}
+
+ void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
+ given: "An initializer built from a Collection of persistent classes"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(
+ ['dataSource.url': 'jdbc:h2:mem:collectionCtor;LOCK_TIMEOUT=10000', 'hibernate.hbm2ddl.auto': 'create'],
+ [Person] as Collection<Class>)
+
+ when: "the application is configured"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+
+ then: "GORM is bootstrapped with the given entity"
+ applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
+ Person.withNewSession { Person.count() == 0 }
+ }
+
+ void "Test configureForDataSource bootstraps GORM around a pre-existing DataSource"() {
+ given: "a DataSource created ahead of time"
+ def dataSource = new DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000', 'sa', '')
+ dataSource.driverClassName = 'org.h2.Driver'
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['hibernate.hbm2ddl.auto': 'create'], Person)
+
+ when: "the initializer is configured around that DataSource"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configureForDataSource(dataSource)
+
+ then: "the pre-existing DataSource is registered and reused rather than a new one being built"
+ applicationContext.getBean(HibernateDatastoreSpringInitializer.DEFAULT_DATA_SOURCE_NAME, DataSource).is(dataSource)
+ Person.withNewSession { Person.count() == 0 }
+ }
+
+ void "Test configureForBeanDefinitionRegistry throws when the hibernateDatastore bean was not registered"() {
+ given: "an initializer whose bean definitions never register hibernateDatastore"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person) {
+ @Override
+ Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) {
+ { -> }
+ }
+ }
+ def registry = new GenericApplicationContext()
+
+ when:
+ datastoreInitializer.configureForBeanDefinitionRegistry(registry)
+
+ then:
+ thrown(IllegalStateException)
+
+ cleanup:
+ registry.close()
+ }
+
+ void "Test the OSIV interceptor is registered when the registry is a web application"() {
+ given: "a registry that signals it belongs to a web application"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['dataSource.url': 'jdbc:h2:mem:osivEnabled;LOCK_TIMEOUT=10000'], Person)
+ def registry = new GenericApplicationContext()
+ registry.registerBeanDefinition('grailsControllerHelper', new RootBeanDefinition(Object))
+
+ when:
+ datastoreInitializer.configureForBeanDefinitionRegistry(registry)
+ registry.refresh()
+ applicationContext = registry
+
+ then:
+ registry.containsBean('openSessionInViewInterceptor')
+ }
+
+ void "Test the OSIV interceptor is not registered for a non-web application registry"() {
+ given: "An initializer instance configured against a plain, non-web registry"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['dataSource.url': 'jdbc:h2:mem:osivDisabled;LOCK_TIMEOUT=10000'], Person)
+
+ when:
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+
+ then:
+ !applicationContext.containsBean('openSessionInViewInterceptor')
+ }
}
@Entity
class Person {
diff --git a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
index e2666d9567..a764e9688a 100644
--- a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
+++ b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
@@ -19,6 +19,7 @@
package grails.mongodb.bootstrap
import com.mongodb.client.MongoClient
+import com.mongodb.client.MongoClients
import grails.mongodb.MongoEntity
import grails.mongodb.geo.Point
import grails.persistence.Entity
@@ -26,6 +27,7 @@ import org.apache.grails.testing.mongo.AutoStartedMongoSpec
import org.bson.Document
import org.grails.datastore.gorm.mongo.Birthday
import org.grails.datastore.gorm.mongo.BirthdayCodec
+import org.grails.datastore.mapping.core.DatastoreUtils
import org.grails.datastore.mapping.engine.types.AbstractMappingAwareCustomTypeMarshaller
import org.grails.datastore.mapping.model.MappingContext
import org.grails.datastore.mapping.model.PersistentProperty
@@ -108,6 +110,50 @@ class MongoDbDataStoreSpringInitializerSpec extends AutoStartedMongoSpec {
mongoDatastore.destroy()
}
+ void "Test configure reuses a pre-existing MongoClient instead of creating a new one"() {
+ given: "a MongoClient created ahead of time"
+ def mongoClient = MongoClients.create("mongodb://${mongoHost}:${mongoPort}".toString())
+ def initializer = makeInitializer([
+ (MongoSettings.SETTING_DATABASE_NAME): 'foo',
+ ], Person)
+ initializer.setMongoClient(mongoClient)
+
+ when: "the initializer is configured"
+ def applicationContext = initializer.configure()
+
+ then: "the pre-existing client is registered and reused rather than a new one being built"
+ applicationContext.getBean('mongo', MongoClient).is(mongoClient)
+ applicationContext.getBean(MongoDatastore).getMongoClient().is(mongoClient)
+
+ cleanup:
+ applicationContext.getBean(MongoDatastore).destroy()
+ mongoClient.close()
+ }
+
+ void "Test the package-scanning constructor discovers entities via classpath scan"() {
+ given: "an initializer configured with a package name rather than explicit classes"
+ def resolver = DatastoreUtils.createPropertyResolver([
+ (MongoSettings.SETTING_HOST): mongoHost,
+ (MongoSettings.SETTING_PORT): mongoPort,
+ ])
+ def initializer = new MongoDbDataStoreSpringInitializer(resolver, Person.package.name) {
+ @Override
+ protected Map<String, Class<?>> loadDataServices(String secondaryDatastore = null) {
+ [:]
+ }
+ }
+
+ when: "the application is configured"
+ def applicationContext = initializer.configure()
+ def mongoDatastore = applicationContext.getBean(MongoDatastore)
+
+ then: "the Person entity declared in the scanned package was discovered and mapped"
+ mongoDatastore.mappingContext.getPersistentEntity(Person.name) != null
+
+ cleanup:
+ mongoDatastore.destroy()
+ }
+
@Issue('GPMONGODB-339')
@Ignore
// The MongoDB API for this test has been altered / removed with no apparent replacement for getting the number of pooled connections in use
diff --git a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
new file mode 100644
index 0000000000..c708ac5c4e
--- /dev/null
+++ b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerUnitSpec.groovy
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package grails.mongodb.bootstrap
+
+import com.mongodb.MongoClientSettings
+import com.mongodb.client.MongoClient
+import grails.mongodb.MongoEntity
+import spock.lang.Specification
+
+/**
+ * Pure unit coverage for {@link MongoDbDataStoreSpringInitializer} that does not require a
+ * running MongoDB instance, covering the {@code isMappedClass} override and the deprecated
+ * bean-style setters that {@link MongoDbDataStoreSpringInitializerSpec} does not reach.
+ */
+class MongoDbDataStoreSpringInitializerUnitSpec extends Specification {
+
+ void 'isMappedClass and collectMappedClasses discriminate MongoEntity classes from unrelated ones for a secondary datastore'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer([MappedThing, UnmappedThing])
+ initializer.setSecondaryDatastore(true)
+
+ expect:
+ initializer.isMappedClass('mongo', MappedThing)
+ !initializer.isMappedClass('mongo', UnmappedThing)
+ initializer.collectMappedClasses('mongo') == [MappedThing]
+ }
+
+ void 'setMongoBeanName updates the mongo bean name'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+
+ when:
+ initializer.setMongoBeanName('customMongo')
+
+ then:
+ initializer.mongoBeanName == 'customMongo'
+ }
+
+ void 'setMongoOptionsBeanName updates the mongo options bean name'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+
+ when:
+ initializer.setMongoOptionsBeanName('customMongoOptions')
+
+ then:
+ initializer.mongoOptionsBeanName == 'customMongoOptions'
+ }
+
+ void 'setDatabaseName updates the database name'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+
+ when:
+ initializer.setDatabaseName('customDb')
+
+ then:
+ initializer.databaseName == 'customDb'
+ }
+
+ void 'setDefaultMapping updates the default mapping closure'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+ def mapping = { -> }
+
+ when:
+ initializer.setDefaultMapping(mapping)
+
+ then:
+ initializer.defaultMapping.is(mapping)
+ }
+
+ void 'setMongoOptions updates the mongo client settings'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+ def settings = MongoClientSettings.builder().build()
+
+ when:
+ initializer.setMongoOptions(settings)
+
+ then:
+ initializer.mongoOptions.is(settings)
+ }
+
+ void 'setMongoClient records the pre-existing client to reuse'() {
+ given:
+ def initializer = new MongoDbDataStoreSpringInitializer()
+ def client = Mock(MongoClient)
+
+ when:
+ initializer.setMongoClient(client)
+
+ then:
+ initializer.mongo.is(client)
+ }
+}
+
+class MappedThing implements MongoEntity<MappedThing> {
+ Long id
+}
+
+class UnmappedThing {
+ static mapWith = 'sql'
+}
(grails-core) 10/10: Fix enableReload being silently ignored; remove dead grailsPlugin property
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit eee486fbc05baffe0bb220037be74d078b8c5f0b
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 17:12:19 2026 -0500
Fix enableReload being silently ignored; remove dead grailsPlugin property
Two different verdicts from git archaeology on these Hibernate5/7
initializer properties:
enableReload (real bug, same class as databaseName/defaultDataSourceBeanName):
HibernateGrailsPlugin sets springInitializer.enableReload =
Environment.isDevelopmentMode() to enable Hibernate's runtime-reload
support by default in dev mode, but the field was never read - it has a
genuine, still-live destination in HibernateConnectionSourceSettings.
enableReload, populated via the same generic PropertyResolver-binding
mechanism proven by the databaseName fix. Adds applyEnableReloadFallback(),
called at the top of getBeanDefinitions() for the same reason as the Mongo
fix, injecting an `enableReload` fallback property only when customized
away from its default and not already explicitly configured. Verified
end-to-end against HibernateDatastore's actual connection source settings,
which also confirms the unprefixed `enableReload` config key empirically
(not just by static tracing).
grailsPlugin (dead, same class as registerApplicationIfNotPresent):
git blame traces this to a 2016 fix (133ca44374) that conditionally kept
GrailsHibernateTransactionManager alive for Grails-plugin bootstrapping
while a parallel refactor moved standalone usage to deriving the
transaction manager from the datastore instead. A follow-up commit five
months later (415d1c6a8e, Jan 2017) dropped the conditional entirely in
favor of the datastore-derived approach unconditionally, but left the
grailsPlugin field and its two HibernateGrailsPlugin call sites behind.
Removed all four references.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../HibernateDatastoreSpringInitializer.groovy | 22 ++++++++++++-
.../plugin/hibernate/HibernateGrailsPlugin.groovy | 1 -
.../HibernateDatastoreSpringInitializerSpec.groovy | 38 ++++++++++++++++++++++
.../HibernateDatastoreSpringInitializer.groovy | 22 ++++++++++++-
.../plugin/hibernate/HibernateGrailsPlugin.groovy | 1 -
.../HibernateDatastoreSpringInitializerSpec.groovy | 38 ++++++++++++++++++++++
6 files changed, 118 insertions(+), 4 deletions(-)
diff --git a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
index 9342779524..66eab17784 100644
--- a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
@@ -23,6 +23,7 @@ import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.support.GenericApplicationContext
import org.springframework.core.env.ConfigurableEnvironment
+import org.springframework.core.env.MapPropertySource
import org.springframework.core.env.PropertyResolver
import org.springframework.transaction.PlatformTransactionManager
@@ -54,7 +55,6 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
String defaultDataSourceBeanName = ConnectionSource.DEFAULT
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
boolean enableReload = false
- boolean grailsPlugin = false
HibernateDatastoreSpringInitializer(PropertyResolver configuration, Collection<Class> persistentClasses) {
super(configuration, persistentClasses)
@@ -128,7 +128,27 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
return applicationContext
}
+ /**
+ * Applies {@link #enableReload} as an {@code enableReload} fallback on {@link #configuration}
+ * when it was customized away from its default and the configuration does not already specify
+ * it explicitly.
+ */
+ protected void applyEnableReloadFallback() {
+ if (!enableReload || configuration.containsProperty('enableReload')) {
+ return
+ }
+ if (configuration instanceof ConfigurableEnvironment) {
+ ((ConfigurableEnvironment) configuration).propertySources.addFirst(
+ new MapPropertySource('hibernateDatastoreSpringInitializer.enableReload', [enableReload: true])
+ )
+ }
+ else if (configuration instanceof Map) {
+ ((Map) configuration).put('enableReload', true)
+ }
+ }
+
Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) {
+ applyEnableReloadFallback()
ApplicationEventPublisher eventPublisher = super.findEventPublisher(beanDefinitionRegistry)
Closure beanDefinitions = {
def common = getCommonConfiguration(beanDefinitionRegistry, 'hibernate')
diff --git a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
index 552eadfd0f..e7944e4fc6 100644
--- a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
@@ -88,7 +88,6 @@ class HibernateGrailsPlugin extends Plugin {
def springInitializer = new HibernateDatastoreSpringInitializer((PropertyResolver) config, domainClasses)
springInitializer.enableReload = Environment.isDevelopmentMode()
- springInitializer.grailsPlugin = true
dataSourceNames = springInitializer.dataSources
def beans = springInitializer.getBeanDefinitions((BeanDefinitionRegistry) applicationContext)
diff --git a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index 760171dfbf..e50b485d0a 100644
--- a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -115,6 +115,44 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
datastoreInitializer.dataSources == ['primary'] as Set<String>
}
+ void "Test applyEnableReloadFallback injects a fallback when customized and not already configured"() {
+ given:
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)
+ datastoreInitializer.enableReload = true
+
+ when:
+ datastoreInitializer.applyEnableReloadFallback()
+
+ then:
+ datastoreInitializer.configuration.getProperty('enableReload', Boolean) == true
+ }
+
+ void "Test applyEnableReloadFallback does not override an already-configured value"() {
+ given:
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['enableReload': 'false'], Person)
+ datastoreInitializer.enableReload = true
+
+ when:
+ datastoreInitializer.applyEnableReloadFallback()
+
+ then:
+ datastoreInitializer.configuration.getProperty('enableReload', Boolean) == false
+ }
+
+ void "Test enableReload is honored end-to-end as a fallback on the default connection's settings"() {
+ given: "an initializer with enableReload customized and no explicit config for it"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(
+ ['dataSource.url': 'jdbc:h2:mem:enableReloadFallback;LOCK_TIMEOUT=10000'], Person)
+ datastoreInitializer.enableReload = true
+
+ when: "the application is configured"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+ def settings = applicationContext.getBean(HibernateDatastore).connectionSources.defaultConnectionSource.settings
+
+ then: "the fallback reaches the datastore's own connection source settings"
+ settings.enableReload
+ }
+
void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
given: "An initializer built from a Collection of persistent classes"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(
diff --git a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
index 2f0b4221ba..d4b7b30c7b 100644
--- a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
@@ -24,6 +24,7 @@ import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.support.GenericApplicationContext
import org.springframework.core.env.ConfigurableEnvironment
+import org.springframework.core.env.MapPropertySource
import org.springframework.core.env.PropertyResolver
import org.springframework.transaction.PlatformTransactionManager
@@ -56,7 +57,6 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
String defaultDataSourceBeanName = ConnectionSource.DEFAULT
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
boolean enableReload = false
- boolean grailsPlugin = false
Closure beanDefinitions
protected ApplicationContext applicationContext
@@ -146,7 +146,27 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
return applicationContext
}
+ /**
+ * Applies {@link #enableReload} as an {@code enableReload} fallback on {@link #configuration}
+ * when it was customized away from its default and the configuration does not already specify
+ * it explicitly.
+ */
+ protected void applyEnableReloadFallback() {
+ if (!enableReload || configuration.containsProperty('enableReload')) {
+ return
+ }
+ if (configuration instanceof ConfigurableEnvironment) {
+ ((ConfigurableEnvironment) configuration).propertySources.addFirst(
+ new MapPropertySource('hibernateDatastoreSpringInitializer.enableReload', [enableReload: true])
+ )
+ }
+ else if (configuration instanceof Map) {
+ ((Map) configuration).put('enableReload', true)
+ }
+ }
+
Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) {
+ applyEnableReloadFallback()
ApplicationEventPublisher eventPublisher = super.findEventPublisher(beanDefinitionRegistry)
return { ->
def common = getCommonConfiguration(beanDefinitionRegistry, 'hibernate')
diff --git a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
index 5b99ed5a06..4e2da888e9 100644
--- a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
@@ -88,7 +88,6 @@ class HibernateGrailsPlugin extends Plugin {
def springInitializer = new HibernateDatastoreSpringInitializer((PropertyResolver) config, domainClasses)
springInitializer.enableReload = Environment.isDevelopmentMode()
- springInitializer.grailsPlugin = true
dataSourceNames = springInitializer.dataSources
def beans = springInitializer.getBeanDefinitions((BeanDefinitionRegistry) applicationContext)
diff --git a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index f8a68aedee..8262cfae53 100644
--- a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -113,6 +113,44 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
datastoreInitializer.dataSources == ['primary'] as Set<String>
}
+ void "Test applyEnableReloadFallback injects a fallback when customized and not already configured"() {
+ given:
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)
+ datastoreInitializer.enableReload = true
+
+ when:
+ datastoreInitializer.applyEnableReloadFallback()
+
+ then:
+ datastoreInitializer.configuration.getProperty('enableReload', Boolean) == true
+ }
+
+ void "Test applyEnableReloadFallback does not override an already-configured value"() {
+ given:
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(['enableReload': 'false'], Person)
+ datastoreInitializer.enableReload = true
+
+ when:
+ datastoreInitializer.applyEnableReloadFallback()
+
+ then:
+ datastoreInitializer.configuration.getProperty('enableReload', Boolean) == false
+ }
+
+ void "Test enableReload is honored end-to-end as a fallback on the default connection's settings"() {
+ given: "an initializer with enableReload customized and no explicit config for it"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(
+ ['dataSource.url': 'jdbc:h2:mem:enableReloadFallback;LOCK_TIMEOUT=10000'], Person)
+ datastoreInitializer.enableReload = true
+
+ when: "the application is configured"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+ def settings = applicationContext.getBean(HibernateDatastore).connectionSources.defaultConnectionSource.settings
+
+ then: "the fallback reaches the datastore's own connection source settings"
+ settings.enableReload
+ }
+
void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
given: "An initializer built from a Collection of persistent classes"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(
(grails-core) 01/10: Add unit coverage for AbstractDatastoreInitializer and clean up IDE warnings
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit f5d0465dac04bbe8c91b58fce8de5fb7d571b26a
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 13:34:44 2026 -0500
Add unit coverage for AbstractDatastoreInitializer and clean up IDE warnings
Introduces a shared TestDatastoreInitializer test double and a new
AbstractDatastoreInitializerSpec covering constructors, event/message
publisher resolution, mapped-class filtering, bean registration helpers,
data-service discovery and a full configure() round trip. The existing
web-application spec is refactored to reuse the shared double instead of
its own private copy.
Also resolves several IDE-flagged issues in AbstractDatastoreInitializer:
replaces the deprecated Class#newInstance() calls with
getDeclaredConstructor().newInstance(), names previously-unused catch
parameters, swaps an equals() call for ==, types the loadDataServices
closure parameters, and makes containsRegisteredBean/getGrailsValidatorClass
static since neither depends on instance state. getCommonConfiguration,
getGrailsApplicationClass and isGrailsPresent are left as instance methods
(with explanatory @SuppressWarnings) since they are genuine override hooks
for downstream datastore initializers.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../bootstrap/AbstractDatastoreInitializer.groovy | 32 +-
.../AbstractDatastoreInitializerSpec.groovy | 360 +++++++++++++++++++++
...ctDatastoreInitializerWebApplicationSpec.groovy | 5 -
.../gorm/bootstrap/TestDatastoreInitializer.groovy | 95 ++++++
4 files changed, 479 insertions(+), 13 deletions(-)
diff --git a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy
index 510c47dcdf..5eb8112edc 100644
--- a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy
+++ b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy
@@ -261,7 +261,14 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
}
}
+ /**
+ * Hook subclasses can override to contribute bean definitions common to every datastore type
+ * they configure. The {@code registry} and {@code type} parameters are unused by this default
+ * no-op implementation but are part of the override contract used by overriders such as the
+ * Hibernate and MongoDB datastore initializers.
+ */
@CompileDynamic
+ @SuppressWarnings(['GrMethodMayBeStatic', 'GroovyUnusedDeclaration'])
Closure getCommonConfiguration(BeanDefinitionRegistry registry, String type) {
return {}
}
@@ -274,7 +281,7 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
}
protected boolean isMappedClass(String datastoreType, Class cls) {
- datastoreType.equals(ClassPropertyFetcher.getStaticPropertyValue(cls, GormProperties.MAPPING_STRATEGY, String))
+ datastoreType == ClassPropertyFetcher.getStaticPropertyValue(cls, GormProperties.MAPPING_STRATEGY, String)
}
abstract Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry)
@@ -311,7 +318,7 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
}
}
loadDataServices(null)
- .each { serviceName, serviceClass ->
+ .each { String serviceName, Class<?> serviceClass ->
"$serviceName"(DatastoreServiceMethodInvokingFactoryBean, serviceClass) {
targetObject = ref("${type}Datastore")
targetMethod = 'getService'
@@ -356,7 +363,7 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
}
@CompileDynamic
- protected boolean containsRegisteredBean(Object builder, BeanDefinitionRegistry registry, String beanName) {
+ protected static boolean containsRegisteredBean(Object builder, BeanDefinitionRegistry registry, String beanName) {
registry.containsBeanDefinition(beanName) || (builder.hasProperty('springConfig') && builder.springConfig.containsBean(beanName))
}
@@ -386,7 +393,13 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
*/
protected abstract Class<AbstractDatastorePersistenceContextInterceptor> getPersistenceInterceptorClass()
+ /**
+ * Not made static: {@code getClass()} intentionally resolves the classloader of the concrete
+ * subclass instance rather than this base class, which matters when a subclass is loaded by a
+ * different (e.g. plugin/OSGi) classloader than {@link AbstractDatastoreInitializer} itself.
+ */
@CompileStatic
+ @SuppressWarnings('GrMethodMayBeStatic')
protected Class getGrailsApplicationClass() {
ClassLoader cl = getClass().getClassLoader()
if (ClassUtils.isPresent('grails.core.DefaultGrailsApplication', cl)) {
@@ -396,6 +409,7 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
}
+ @SuppressWarnings('GrMethodMayBeStatic')
protected boolean isGrailsPresent() {
ClassLoader cl = getClass().getClassLoader()
if (ClassUtils.isPresent('grails.core.DefaultGrailsApplication', cl)) {
@@ -405,7 +419,7 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
}
@CompileStatic
- protected Class getGrailsValidatorClass() {
+ protected static Class getGrailsValidatorClass() {
throw new UnsupportedOperationException('Method getGrailsValidatorClass no longer supported')
}
@@ -415,13 +429,14 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
try {
Thread.currentThread().contextClassLoader.loadClass('org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader')
return true
- } catch (e) {
+ } catch (ignored) {
return false
}
}
static void registerBeans(BeanDefinitionRegistry registry, Closure beanDefinitions) {
def classLoader = Thread.currentThread().contextClassLoader
- def beanReader = classLoader.loadClass('org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader').newInstance(registry)
+ def readerClass = classLoader.loadClass('org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader')
+ def beanReader = readerClass.getDeclaredConstructor(BeanDefinitionRegistry).newInstance(registry)
beanReader.beans(beanDefinitions)
}
}
@@ -432,14 +447,15 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
try {
Thread.currentThread().contextClassLoader.loadClass('grails.spring.BeanBuilder')
return true
- } catch (e) {
+ } catch (ignored) {
return false
}
}
static void registerBeans(BeanDefinitionRegistry registry, Closure beanDefinitions) {
def classLoader = Thread.currentThread().contextClassLoader
- def beanBuilder = classLoader.loadClass('grails.spring.BeanBuilder').newInstance()
+ def beanBuilderClass = classLoader.loadClass('grails.spring.BeanBuilder')
+ def beanBuilder = beanBuilderClass.getDeclaredConstructor().newInstance()
beanBuilder.beans(beanDefinitions)
beanBuilder.registerBeans(registry)
}
diff --git a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy
new file mode 100644
index 0000000000..076f2445bb
--- /dev/null
+++ b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy
@@ -0,0 +1,360 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.grails.datastore.gorm.bootstrap
+
+import org.springframework.beans.factory.support.DefaultListableBeanFactory
+import org.springframework.beans.factory.support.RootBeanDefinition
+import org.springframework.context.support.GenericApplicationContext
+import org.springframework.context.support.StaticMessageSource
+import org.springframework.core.env.StandardEnvironment
+import spock.lang.Specification
+
+import grails.core.DefaultGrailsApplication
+import org.grails.datastore.gorm.events.ConfigurableApplicationContextEventPublisher
+import org.grails.datastore.gorm.events.DefaultApplicationEventPublisher
+import org.grails.datastore.gorm.services.DefaultTenantService
+import org.grails.datastore.gorm.services.DefaultTransactionService
+
+/**
+ * Unit coverage for the reusable configuration behaviour in {@link AbstractDatastoreInitializer},
+ * exercised through the {@link TestDatastoreInitializer} test double so no real datastore module
+ * (Hibernate, MongoDB, Neo4j, ...) is required.
+ */
+class AbstractDatastoreInitializerSpec extends Specification {
+
+ void 'the no-arg constructor uses sensible defaults'() {
+ when:
+ def initializer = new TestDatastoreInitializer()
+
+ then:
+ initializer.packages == []
+ initializer.persistentClasses == []
+ initializer.configuration instanceof StandardEnvironment
+ initializer.originalConfiguration == null
+ initializer.registerApplicationIfNotPresent
+ }
+
+ void 'a package-name constructor records the given packages'() {
+ when:
+ def initializer = new TestDatastoreInitializer('com.example', 'com.other')
+
+ then:
+ initializer.packages == ['com.example', 'com.other']
+ }
+
+ void 'a persistent-class constructor records the given classes'() {
+ when:
+ def initializer = new TestDatastoreInitializer(String, Integer)
+
+ then:
+ initializer.persistentClasses == [String, Integer]
+ }
+
+ void 'a Map configuration constructor derives a PropertyResolver but retains the original Map'() {
+ given:
+ Map config = ['foo.bar': 'baz']
+
+ when:
+ def initializer = new TestDatastoreInitializer(config, [String])
+
+ then:
+ initializer.originalConfiguration.is(config)
+ initializer.configuration.getRequiredProperty('foo.bar') == 'baz'
+ initializer.persistentClasses == [String]
+ }
+
+ void 'a PropertyResolver configuration constructor keeps the resolver as-is with no original configuration'() {
+ given:
+ def resolver = new StandardEnvironment()
+
+ when:
+ def initializer = new TestDatastoreInitializer(resolver, [String])
+
+ then:
+ initializer.configuration.is(resolver)
+ initializer.originalConfiguration == null
+ }
+
+ void 'findEventPublisher wraps the registry itself when it is a ConfigurableApplicationContext'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+ def context = new GenericApplicationContext()
+
+ expect:
+ initializer.findEventPublisher(context) instanceof ConfigurableApplicationContextEventPublisher
+
+ cleanup:
+ context.close()
+ }
+
+ void 'findEventPublisher falls back to the resource loader when the registry is not a ConfigurableApplicationContext'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+ def context = new GenericApplicationContext()
+ initializer.setResourceLoader(context)
+
+ expect:
+ initializer.findEventPublisher(new DefaultListableBeanFactory()) instanceof ConfigurableApplicationContextEventPublisher
+
+ cleanup:
+ context.close()
+ }
+
+ void 'findEventPublisher defaults to a DefaultApplicationEventPublisher when neither source is available'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ expect:
+ initializer.findEventPublisher(new DefaultListableBeanFactory()) instanceof DefaultApplicationEventPublisher
+ }
+
+ void 'findMessageSource uses the registry itself when it is a MessageSource'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+ def context = new GenericApplicationContext()
+
+ expect:
+ initializer.findMessageSource(context).is(context)
+
+ cleanup:
+ context.close()
+ }
+
+ void 'findMessageSource falls back to the resource loader when the registry is not a MessageSource'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+ def context = new GenericApplicationContext()
+ initializer.setResourceLoader(context)
+
+ expect:
+ initializer.findMessageSource(new DefaultListableBeanFactory()).is(context)
+
+ cleanup:
+ context.close()
+ }
+
+ void 'findMessageSource defaults to a fresh StaticMessageSource when neither source is available'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ expect:
+ initializer.findMessageSource(new DefaultListableBeanFactory()) instanceof StaticMessageSource
+ }
+
+ void 'setResourceLoader rebuilds the resource pattern resolver around the given loader'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+ def loader = new GenericApplicationContext()
+
+ when:
+ initializer.setResourceLoader(loader)
+
+ then:
+ initializer.resourcePatternResolver.resourceLoader.is(loader)
+
+ cleanup:
+ loader.close()
+ }
+
+ void 'isMappedClass returns true only when the static mapWith property matches the datastore type'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ expect:
+ initializer.isMappedClass('mongo', MongoEntity)
+ !initializer.isMappedClass('sql', MongoEntity)
+ !initializer.isMappedClass('mongo', UnmappedEntity)
+ }
+
+ void 'collectMappedClasses returns every persistent class when this is not a secondary datastore'() {
+ given:
+ def initializer = new TestDatastoreInitializer([MongoEntity, SqlEntity, UnmappedEntity])
+
+ expect:
+ initializer.collectMappedClasses('mongo') == [MongoEntity, SqlEntity, UnmappedEntity]
+ }
+
+ void 'collectMappedClasses filters to only the classes mapped to the given type for a secondary datastore'() {
+ given:
+ def initializer = new TestDatastoreInitializer([MongoEntity, SqlEntity, UnmappedEntity])
+ initializer.setSecondaryDatastore(true)
+
+ expect:
+ initializer.collectMappedClasses('mongo') == [MongoEntity]
+ initializer.collectMappedClasses('sql') == [SqlEntity]
+ }
+
+ void 'containsRegisteredBean returns true when the registry already contains a bean definition with that name'() {
+ given:
+ def registry = new DefaultListableBeanFactory()
+ registry.registerBeanDefinition('fooBean', new RootBeanDefinition(Object))
+ def initializer = new TestDatastoreInitializer()
+
+ expect:
+ initializer.containsRegisteredBean(new Object(), registry, 'fooBean')
+ }
+
+ void 'containsRegisteredBean falls back to a springConfig-aware builder when the registry does not know the bean'() {
+ given:
+ def registry = new DefaultListableBeanFactory()
+ def builder = new BeanBuilderStub(springConfig: new SpringConfigStub(beanNames: ['fooBean'] as Set))
+ def initializer = new TestDatastoreInitializer()
+
+ expect:
+ initializer.containsRegisteredBean(builder, registry, 'fooBean')
+ !initializer.containsRegisteredBean(builder, registry, 'otherBean')
+ }
+
+ void 'containsRegisteredBean returns false when neither the registry nor the builder know the bean'() {
+ given:
+ def registry = new DefaultListableBeanFactory()
+ def initializer = new TestDatastoreInitializer()
+
+ expect:
+ !initializer.containsRegisteredBean(new Object(), registry, 'fooBean')
+ }
+
+ void 'getCommonConfiguration returns a no-op closure by default'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ when:
+ def closure = initializer.getCommonConfiguration(new DefaultListableBeanFactory(), 'foo')
+
+ then:
+ closure instanceof Closure
+ closure() == null
+ }
+
+ void 'loadDataServices discovers the Service implementations declared for this module'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ when:
+ def services = initializer.loadDataServices()
+
+ then:
+ services.defaultTransactionService == DefaultTransactionService
+ services.defaultTenantService == DefaultTenantService
+ }
+
+ void 'loadDataServices namespaces service names under the secondary datastore type when given'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ when:
+ def services = initializer.loadDataServices('foo')
+
+ then:
+ services.fooDefaultTransactionService == DefaultTransactionService
+ services.fooDefaultTenantService == DefaultTenantService
+ }
+
+ void 'isGrailsPresent and getGrailsApplicationClass detect grails-core on the classpath'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ expect:
+ initializer.isGrailsPresent()
+ initializer.getGrailsApplicationClass() == DefaultGrailsApplication
+ }
+
+ void 'getGrailsValidatorClass is no longer supported'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+
+ when:
+ initializer.getGrailsValidatorClass()
+
+ then:
+ thrown(UnsupportedOperationException)
+ }
+
+ void 'getAdditionalBeansConfiguration registers a transaction manager, persistence interceptor, aggregator and every data service'() {
+ given:
+ def registry = new DefaultListableBeanFactory()
+ def initializer = new TestDatastoreInitializer()
+
+ when:
+ def beanDefinitions = initializer.getAdditionalBeansConfiguration(registry, 'foo')
+ AbstractDatastoreInitializer.GroovyBeanReaderInit.registerBeans(registry, beanDefinitions)
+
+ then:
+ registry.containsBeanDefinition('fooTransactionManager')
+ registry.isAlias('transactionManager')
+ registry.getAliases('fooTransactionManager') as Set == ['transactionManager'] as Set
+ registry.containsBeanDefinition('fooPersistenceInterceptor')
+ registry.containsBeanDefinition('fooPersistenceContextInterceptorAggregator')
+ registry.containsBeanDefinition('defaultTransactionService')
+ registry.containsBeanDefinition('defaultTenantService')
+ !registry.containsBeanDefinition('fooOpenSessionInViewInterceptor')
+ }
+
+ void 'getAdditionalBeansConfiguration does not alias an already-registered transactionManager bean'() {
+ given:
+ def registry = new DefaultListableBeanFactory()
+ registry.registerBeanDefinition('transactionManager', new RootBeanDefinition(Object))
+ def initializer = new TestDatastoreInitializer()
+
+ when:
+ def beanDefinitions = initializer.getAdditionalBeansConfiguration(registry, 'foo')
+ AbstractDatastoreInitializer.GroovyBeanReaderInit.registerBeans(registry, beanDefinitions)
+
+ then:
+ registry.containsBeanDefinition('fooTransactionManager')
+ !registry.isAlias('transactionManager')
+ registry.getBeanDefinition('transactionManager').beanClassName == Object.name
+ }
+
+ void 'configure builds a fully refreshed application context containing the declared beans'() {
+ given:
+ def initializer = new TestDatastoreInitializer()
+ initializer.beanDefinitions = { -> "sampleBean"(String, 'hello') }
+
+ when:
+ def context = initializer.configure()
+
+ then:
+ context.isActive()
+ context.getBean('sampleBean', String) == 'hello'
+
+ cleanup:
+ context.close()
+ }
+
+ static class MongoEntity {
+ static mapWith = 'mongo'
+ }
+
+ static class SqlEntity {
+ static mapWith = 'sql'
+ }
+
+ static class UnmappedEntity {
+ }
+
+ static class SpringConfigStub {
+ Set<String> beanNames
+ boolean containsBean(String name) { name in beanNames }
+ }
+
+ static class BeanBuilderStub {
+ SpringConfigStub springConfig
+ }
+}
diff --git a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerWebApplicationSpec.groovy b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerWebApplicationSpec.groovy
index 0a76590f3a..7d77719c2d 100644
--- a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerWebApplicationSpec.groovy
+++ b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerWebApplicationSpec.groovy
@@ -57,9 +57,4 @@ class AbstractDatastoreInitializerWebApplicationSpec extends Specification {
expect:
!isWeb(new DefaultListableBeanFactory())
}
-
- static class TestDatastoreInitializer extends AbstractDatastoreInitializer {
- Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) { { -> } }
- protected Class getPersistenceInterceptorClass() { null }
- }
}
diff --git a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/TestDatastoreInitializer.groovy b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/TestDatastoreInitializer.groovy
new file mode 100644
index 0000000000..a051d2792e
--- /dev/null
+++ b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/TestDatastoreInitializer.groovy
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.grails.datastore.gorm.bootstrap
+
+import org.springframework.beans.factory.support.BeanDefinitionRegistry
+import org.springframework.core.env.PropertyResolver
+
+import org.grails.datastore.gorm.support.AbstractDatastorePersistenceContextInterceptor
+import org.grails.datastore.mapping.core.Datastore
+
+/**
+ * Minimal concrete {@link AbstractDatastoreInitializer} used to exercise the abstract
+ * class's own behaviour in unit tests without depending on a real datastore module
+ * (Hibernate, MongoDB, Neo4j, ...).
+ *
+ * <p>{@link #beanDefinitions} and {@link #persistenceInterceptorClass} default to
+ * harmless no-op implementations but can be overridden per-test.
+ */
+class TestDatastoreInitializer extends AbstractDatastoreInitializer {
+
+ Closure beanDefinitions = { -> }
+ Class<AbstractDatastorePersistenceContextInterceptor> persistenceInterceptorClass = TestPersistenceContextInterceptor
+
+ TestDatastoreInitializer() {
+ super()
+ }
+
+ TestDatastoreInitializer(ClassLoader classLoader, String... packages) {
+ super(classLoader, packages)
+ }
+
+ TestDatastoreInitializer(String... packages) {
+ super(packages)
+ }
+
+ TestDatastoreInitializer(Collection<Class> persistentClasses) {
+ super(persistentClasses)
+ }
+
+ TestDatastoreInitializer(Class... persistentClasses) {
+ super(persistentClasses)
+ }
+
+ TestDatastoreInitializer(PropertyResolver configuration, Collection<Class> persistentClasses) {
+ super(configuration, persistentClasses)
+ }
+
+ TestDatastoreInitializer(PropertyResolver configuration, Class... persistentClasses) {
+ super(configuration, persistentClasses)
+ }
+
+ TestDatastoreInitializer(PropertyResolver configuration, String... packages) {
+ super(configuration, packages)
+ }
+
+ TestDatastoreInitializer(Map configuration, Collection<Class> persistentClasses) {
+ super(configuration, persistentClasses)
+ }
+
+ TestDatastoreInitializer(Map configuration, Class... persistentClasses) {
+ super(configuration, persistentClasses)
+ }
+
+ @Override
+ Closure getBeanDefinitions(BeanDefinitionRegistry beanDefinitionRegistry) {
+ beanDefinitions
+ }
+
+ @Override
+ protected Class<AbstractDatastorePersistenceContextInterceptor> getPersistenceInterceptorClass() {
+ persistenceInterceptorClass
+ }
+
+ static class TestPersistenceContextInterceptor extends AbstractDatastorePersistenceContextInterceptor {
+ TestPersistenceContextInterceptor(Datastore datastore) {
+ super(datastore)
+ }
+ }
+}
(grails-core) 07/10: Fix defaultDataSourceBeanName being hardcoded around instead of used
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 4d44c4a60bb47088512862affbd09a61f06fd900
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 15:57:08 2026 -0500
Fix defaultDataSourceBeanName being hardcoded around instead of used
Same bug class as the mongoBeanName fix: configureDataSources() correctly
used defaultDataSourceBeanName in its null-config fallback, but hardcoded
the literal ConnectionSource.DEFAULT in the config-present branch (and, on
Hibernate7, in the per-datasource bean-registration loop guard too). No
current caller customizes this property, so today's behavior is
unaffected, but the property is now internally consistent wherever it's
referenced.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../bootstrap/HibernateDatastoreSpringInitializer.groovy | 2 +-
.../HibernateDatastoreSpringInitializerSpec.groovy | 12 ++++++++++++
.../bootstrap/HibernateDatastoreSpringInitializer.groovy | 4 ++--
.../HibernateDatastoreSpringInitializerSpec.groovy | 13 +++++++++++++
4 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
index 0f36a69057..9342779524 100644
--- a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
@@ -97,7 +97,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
}
Map dataSource = (Map) config.getProperty(DEFAULT_DATA_SOURCE_NAME, Map, Collections.emptyMap())
if (dataSource != null && !dataSource.isEmpty()) {
- dataSourceNames.add(ConnectionSource.DEFAULT)
+ dataSourceNames.add(defaultDataSourceBeanName)
}
}
this.dataSources = dataSourceNames
diff --git a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index bd37fd7fe6..760171dfbf 100644
--- a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -103,6 +103,18 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
}
+ void "Test configureDataSources uses the customized default data source bean name consistently"() {
+ given: "an initializer with a customized default data source bean name"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)
+ datastoreInitializer.defaultDataSourceBeanName = 'primary'
+
+ when: "data sources are configured from a resolver with only the default data source present"
+ datastoreInitializer.configureDataSources(DatastoreUtils.createPropertyResolver(['dataSource.url': 'jdbc:h2:mem:customDefaultDsName;LOCK_TIMEOUT=10000']))
+
+ then: "the default entry is recorded under the custom name, not the literal ConnectionSource.DEFAULT"
+ datastoreInitializer.dataSources == ['primary'] as Set<String>
+ }
+
void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
given: "An initializer built from a Collection of persistent classes"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(
diff --git a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
index 327729e083..2f0b4221ba 100644
--- a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
@@ -101,7 +101,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
}
Map dataSource = (Map) config.getProperty(DEFAULT_DATA_SOURCE_NAME, Map, Collections.emptyMap())
if (dataSource != null && !dataSource.isEmpty()) {
- dataSourceNames.add(ConnectionSource.DEFAULT)
+ dataSourceNames.add(defaultDataSourceBeanName)
}
}
this.dataSources = dataSourceNames
@@ -182,7 +182,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
getBeanDefinition('transactionManager').beanClass = PlatformTransactionManager
for (String dataSourceName in dataSources) {
- if (dataSourceName == ConnectionSource.DEFAULT) continue
+ if (dataSourceName == defaultDataSourceBeanName) continue
"dataSource_$dataSourceName"(hibernateDatastore: 'getDataSource', dataSourceName)
"sessionFactory_$dataSourceName"(hibernateDatastore: 'getSessionFactory', dataSourceName)
diff --git a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index 8fbeeb238a..f8a68aedee 100644
--- a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -19,6 +19,7 @@
package grails.orm.bootstrap
import grails.gorm.annotation.Entity
+import org.grails.datastore.mapping.core.DatastoreUtils
import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.SessionFactory
@@ -100,6 +101,18 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
}
}
+ void "Test configureDataSources uses the customized default data source bean name consistently"() {
+ given: "an initializer with a customized default data source bean name"
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer([:], Person)
+ datastoreInitializer.defaultDataSourceBeanName = 'primary'
+
+ when: "data sources are configured from a resolver with only the default data source present"
+ datastoreInitializer.configureDataSources(DatastoreUtils.createPropertyResolver(['dataSource.url': 'jdbc:h2:mem:customDefaultDsName;LOCK_TIMEOUT=10000']))
+
+ then: "the default entry is recorded under the custom name, not the literal ConnectionSource.DEFAULT"
+ datastoreInitializer.dataSources == ['primary'] as Set<String>
+ }
+
void "Test the Map/Collection<Class> constructor bootstraps GORM"() {
given: "An initializer built from a Collection of persistent classes"
def datastoreInitializer = new HibernateDatastoreSpringInitializer(
(grails-core) 06/10: Fix setMongoBeanName being silently ignored when building a new MongoClient
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 393464309e901e5a2689f9a5861842c0ebedac6d
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 15:09:55 2026 -0500
Fix setMongoBeanName being silently ignored when building a new MongoClient
The mongo == null branch of getBeanDefinitions() registered the built
MongoClient under a hardcoded 'mongo' bean name instead of the
customizable mongoBeanName field, unlike configure()'s mongo != null
branch which already honored it. Beyond fixing the inconsistency, this
also resolves an IDE warning where the literal mongo(...) DSL call was
being confused with the protected 'mongo' field of the same name.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../MongoDbDataStoreSpringInitializer.groovy | 2 +-
.../MongoDbDataStoreSpringInitializerSpec.groovy | 20 ++++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy b/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy
index 68dfe37d57..7672e0fe8e 100644
--- a/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy
+++ b/grails-data-mongodb/core/src/main/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializer.groovy
@@ -104,7 +104,7 @@ class MongoDbDataStoreSpringInitializer extends AbstractDatastoreInitializer {
bean.autowire = true
}
mongoDatastore(MongoDatastore, configuration, ref('mongoConnectionSourceFactory'), eventPublisher, collectMappedClasses(DATASTORE_TYPE))
- mongo(mongoDatastore: 'getMongoClient')
+ "$mongoBeanName"(mongoDatastore: 'getMongoClient')
}
else {
mongoDatastore(MongoDatastore, mongo, configuration, eventPublisher, collectMappedClasses(DATASTORE_TYPE))
diff --git a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
index a764e9688a..300d447ab7 100644
--- a/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
+++ b/grails-data-mongodb/core/src/test/groovy/grails/mongodb/bootstrap/MongoDbDataStoreSpringInitializerSpec.groovy
@@ -75,6 +75,26 @@ class MongoDbDataStoreSpringInitializerSpec extends AutoStartedMongoSpec {
Person.count() == 0
}
+ void "Test setMongoBeanName is honored when building a new MongoClient from scratch"() {
+ given: "an initializer with a customized mongo bean name"
+ def initializer = makeInitializer([
+ (MongoSettings.SETTING_HOST): mongoHost,
+ (MongoSettings.SETTING_PORT): mongoPort,
+ ], Person)
+ initializer.setMongoBeanName('customMongo')
+
+ when: "the initializer is configured"
+ def applicationContext = initializer.configure()
+ def mongoDatastore = applicationContext.getBean(MongoDatastore)
+
+ then: "the client is registered under the customized bean name and not the default"
+ applicationContext.getBean('customMongo', MongoClient) == mongoDatastore.getMongoClient()
+ !applicationContext.containsBean('mongo')
+
+ cleanup:
+ mongoDatastore.destroy()
+ }
+
void "Test the alias is created when it is the primary datastore"() {
when: "the initializer used to setup GORM for MongoDB"
def initializer = makeInitializer([
(grails-core) 09/10: Remove dead registerApplicationIfNotPresent property
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit ca684d47124a77185efc7a72f44c5f2f98f503de
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 17:03:55 2026 -0500
Remove dead registerApplicationIfNotPresent property
Git archaeology (3eb036d4cc, "Simplify AbstractDatastoreInitializer",
Nov 2018) shows this flag gated registration of a legacy grailsApplication
Spring bean needed for pre-3.3 Grails compatibility - and that logic's own
first line already made it a no-op for Grails 3.3+ (released 2017). The
2018 commit correctly deleted the dead logic but left the now-meaningless
field behind, and the four plugins that set it were never updated. Removes
the field and all four call sites (Hibernate5, Hibernate7, MongoDB, and
the already-unbuildable Neo4j module, fixed for source-tree consistency).
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy | 1 -
.../src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy | 1 -
.../src/main/groovy/grails/plugins/mongodb/MongodbGrailsPlugin.groovy | 1 -
.../main/groovy/org/grails/datastore/gorm/neo4j/Neo4jGrailsPlugin.groovy | 1 -
.../grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy | 1 -
.../datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy | 1 -
6 files changed, 6 deletions(-)
diff --git a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
index 366da3bc46..552eadfd0f 100644
--- a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
@@ -88,7 +88,6 @@ class HibernateGrailsPlugin extends Plugin {
def springInitializer = new HibernateDatastoreSpringInitializer((PropertyResolver) config, domainClasses)
springInitializer.enableReload = Environment.isDevelopmentMode()
- springInitializer.registerApplicationIfNotPresent = false
springInitializer.grailsPlugin = true
dataSourceNames = springInitializer.dataSources
def beans = springInitializer.getBeanDefinitions((BeanDefinitionRegistry) applicationContext)
diff --git a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
index b758e72302..5b99ed5a06 100644
--- a/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/main/groovy/grails/plugin/hibernate/HibernateGrailsPlugin.groovy
@@ -88,7 +88,6 @@ class HibernateGrailsPlugin extends Plugin {
def springInitializer = new HibernateDatastoreSpringInitializer((PropertyResolver) config, domainClasses)
springInitializer.enableReload = Environment.isDevelopmentMode()
- springInitializer.registerApplicationIfNotPresent = false
springInitializer.grailsPlugin = true
dataSourceNames = springInitializer.dataSources
def beans = springInitializer.getBeanDefinitions((BeanDefinitionRegistry) applicationContext)
diff --git a/grails-data-mongodb/grails-plugin/src/main/groovy/grails/plugins/mongodb/MongodbGrailsPlugin.groovy b/grails-data-mongodb/grails-plugin/src/main/groovy/grails/plugins/mongodb/MongodbGrailsPlugin.groovy
index 638db71d3d..971c137789 100644
--- a/grails-data-mongodb/grails-plugin/src/main/groovy/grails/plugins/mongodb/MongodbGrailsPlugin.groovy
+++ b/grails-data-mongodb/grails-plugin/src/main/groovy/grails/plugins/mongodb/MongodbGrailsPlugin.groovy
@@ -53,7 +53,6 @@ class MongodbGrailsPlugin extends Plugin {
Closure doWithSpring() {
ConfigSupport.prepareConfig(config, (ConfigurableApplicationContext) applicationContext)
def initializer = new MongoDbDataStoreSpringInitializer((PropertyResolver) config, grailsApplication.getArtefacts(DomainClassArtefactHandler.TYPE).collect() { GrailsClass cls -> cls.clazz })
- initializer.registerApplicationIfNotPresent = false
def applicationName = Metadata.getCurrent().getApplicationName()
if (!applicationName.contains('@')) {
diff --git a/grails-data-neo4j/grails-plugin/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jGrailsPlugin.groovy b/grails-data-neo4j/grails-plugin/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jGrailsPlugin.groovy
index fd0d7b5908..7ca307fda0 100644
--- a/grails-data-neo4j/grails-plugin/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jGrailsPlugin.groovy
+++ b/grails-data-neo4j/grails-plugin/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jGrailsPlugin.groovy
@@ -65,7 +65,6 @@ class Neo4jGrailsPlugin extends Plugin {
Closure doWithSpring() {
ConfigSupport.prepareConfig(config, (ConfigurableApplicationContext) applicationContext)
def initializer = new Neo4jDataStoreSpringInitializer((PropertyResolver) config, grailsApplication.getArtefacts(DomainClassArtefactHandler.TYPE).collect() { GrailsClass cls -> cls.clazz })
- initializer.registerApplicationIfNotPresent = false
initializer.setSecondaryDatastore(hasHibernatePlugin())
return initializer.getBeanDefinitions((BeanDefinitionRegistry)applicationContext)
}
diff --git a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy
index 5eb8112edc..5c24120da2 100644
--- a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy
+++ b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializer.groovy
@@ -77,7 +77,6 @@ abstract class AbstractDatastoreInitializer implements ResourceLoaderAware {
Collection<Class> persistentClasses = []
Collection<String> packages = []
PropertyResolver configuration = new StandardEnvironment()
- boolean registerApplicationIfNotPresent = true
Object originalConfiguration
protected ClassLoader classLoader = Thread.currentThread().contextClassLoader
diff --git a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy
index 076f2445bb..547f8c527d 100644
--- a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy
+++ b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/bootstrap/AbstractDatastoreInitializerSpec.groovy
@@ -47,7 +47,6 @@ class AbstractDatastoreInitializerSpec extends Specification {
initializer.persistentClasses == []
initializer.configuration instanceof StandardEnvironment
initializer.originalConfiguration == null
- initializer.registerApplicationIfNotPresent
}
void 'a package-name constructor records the given packages'() {
(grails-core) 03/10: Fix H5 HibernateDatastoreSpringInitializer warnings and add PropertyResolver constructor coverage
Posted by bo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch test/abstract-datastore-initializer
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 6236d0cbff50dde5f05d500c0d174138152a65ab
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Aug 13 14:56:58 2026 -0500
Fix H5 HibernateDatastoreSpringInitializer warnings and add PropertyResolver constructor coverage
Fixes a real unchecked-cast issue in getPersistenceInterceptorClass() (it
was missing the cast that the Hibernate7 sibling already has), and removes
two genuinely dead members: the never-invoked getTestDbUrl() method and the
defaultSessionFactoryBeanName property, whose value was never actually read
by the sessionFactory bean registration it appeared to configure.
Rather than silence the "unused constructor" warnings on the
PropertyResolver-based constructors, adds direct tests for all three - they
are exercised in production only through a dynamically-typed call site in
HibernateGrailsPlugin, which static analysis can't resolve to a specific
overload. Also swaps a couple of ad-hoc H2 URLs (introduced in the prior
commit, one already merged for hibernate7) for the class's own TEST_DB_URL
constant.
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../HibernateDatastoreSpringInitializer.groovy | 7 +--
.../HibernateDatastoreSpringInitializerSpec.groovy | 52 +++++++++++++++++++++-
.../HibernateDatastoreSpringInitializerSpec.groovy | 2 +-
3 files changed, 53 insertions(+), 8 deletions(-)
diff --git a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
index 62727e13dc..b5ee5ab4f2 100644
--- a/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/main/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializer.groovy
@@ -53,7 +53,6 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
public static final String TEST_DB_URL = 'jdbc:h2:mem:grailsDb;LOCK_TIMEOUT=10000;DB_CLOSE_DELAY=-1'
String defaultDataSourceBeanName = ConnectionSource.DEFAULT
- String defaultSessionFactoryBeanName = SESSION_FACTORY_BEAN_NAME
Set<String> dataSources = [defaultDataSourceBeanName] as Set<String>
boolean enableReload = false
boolean grailsPlugin = false
@@ -107,7 +106,7 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
@Override
protected Class<AbstractDatastorePersistenceContextInterceptor> getPersistenceInterceptorClass() {
- getClass().classLoader.loadClass('org.grails.plugin.hibernate.support.HibernatePersistenceContextInterceptor')
+ getClass().classLoader.loadClass('org.grails.plugin.hibernate.support.HibernatePersistenceContextInterceptor') as Class<AbstractDatastorePersistenceContextInterceptor>
}
/**
@@ -121,10 +120,6 @@ class HibernateDatastoreSpringInitializer extends AbstractDatastoreInitializer {
return applicationContext
}
- protected String getTestDbUrl() {
- TEST_DB_URL
- }
-
@CompileStatic
ApplicationContext configureForDataSource(DataSource dataSource) {
GenericApplicationContext applicationContext = createApplicationContext()
diff --git a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index f2e542589b..bd37fd7fe6 100644
--- a/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate5/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -19,6 +19,7 @@
package grails.orm.bootstrap
import grails.gorm.annotation.Entity
+import org.grails.datastore.mapping.core.DatastoreUtils
import org.grails.orm.hibernate.HibernateDatastore
import org.hibernate.Session
import org.hibernate.SessionFactory
@@ -116,9 +117,58 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
Person.withNewSession { Person.count() == 0 }
}
+ void "Test the PropertyResolver/Collection<Class> constructor bootstraps GORM"() {
+ given: "An initializer built from a PropertyResolver and a Collection of persistent classes"
+ def resolver = DatastoreUtils.createPropertyResolver([
+ 'dataSource.url' : 'jdbc:h2:mem:propertyResolverCollectionCtor;LOCK_TIMEOUT=10000',
+ 'hibernate.hbm2ddl.auto': 'create'
+ ])
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(resolver, [Person] as Collection<Class>)
+
+ when: "the application is configured"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+
+ then: "GORM is bootstrapped with the given entity"
+ applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
+ Person.withNewSession { Person.count() == 0 }
+ }
+
+ void "Test the PropertyResolver/Class... constructor bootstraps GORM"() {
+ given: "An initializer built from a PropertyResolver and an array of persistent classes"
+ def resolver = DatastoreUtils.createPropertyResolver([
+ 'dataSource.url' : 'jdbc:h2:mem:propertyResolverClassCtor;LOCK_TIMEOUT=10000',
+ 'hibernate.hbm2ddl.auto': 'create'
+ ])
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(resolver, Person)
+
+ when: "the application is configured"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+
+ then: "GORM is bootstrapped with the given entity"
+ applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
+ Person.withNewSession { Person.count() == 0 }
+ }
+
+ void "Test the PropertyResolver/String... packages constructor discovers entities via classpath scan"() {
+ given: "an initializer configured with a package name rather than explicit classes; the scan also picks up Book and Author, which require the 'books'/'moreBooks' datasources"
+ def resolver = DatastoreUtils.createPropertyResolver([
+ 'dataSource.url' : 'jdbc:h2:mem:propertyResolverPackageCtor;LOCK_TIMEOUT=10000',
+ 'hibernate.hbm2ddl.auto': 'create',
+ 'dataSources.books.url' : 'jdbc:h2:mem:propertyResolverPackageCtorBooks;LOCK_TIMEOUT=10000',
+ 'dataSources.moreBooks.url': 'jdbc:h2:mem:propertyResolverPackageCtorMoreBooks;LOCK_TIMEOUT=10000'
+ ])
+ def datastoreInitializer = new HibernateDatastoreSpringInitializer(resolver, Person.package.name)
+
+ when: "the application is configured"
+ applicationContext = (ConfigurableApplicationContext) datastoreInitializer.configure()
+
+ then: "the Person entity declared in the scanned package was discovered and mapped"
+ applicationContext.getBean(HibernateDatastore).mappingContext.getPersistentEntity(Person.name) != null
+ }
+
void "Test configureForDataSource bootstraps GORM around a pre-existing DataSource"() {
given: "a DataSource created ahead of time"
- def dataSource = new DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000', 'sa', '')
+ def dataSource = new DriverManagerDataSource(HibernateDatastoreSpringInitializer.TEST_DB_URL, 'sa', '')
dataSource.driverClassName = 'org.h2.Driver'
def datastoreInitializer = new HibernateDatastoreSpringInitializer(['hibernate.hbm2ddl.auto': 'create'], Person)
diff --git a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
index afa580120f..8fbeeb238a 100644
--- a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
+++ b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy
@@ -116,7 +116,7 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{
void "Test configureForDataSource bootstraps GORM around a pre-existing DataSource"() {
given: "a DataSource created ahead of time"
- def dataSource = new DriverManagerDataSource('jdbc:h2:mem:configureForDataSource;LOCK_TIMEOUT=10000', 'sa', '')
+ def dataSource = new DriverManagerDataSource(HibernateDatastoreSpringInitializer.TEST_DB_URL, 'sa', '')
dataSource.driverClassName = 'org.h2.Driver'
def datastoreInitializer = new HibernateDatastoreSpringInitializer(['hibernate.hbm2ddl.auto': 'create'], Person)