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/03/09 18:04:37 UTC
(grails-core) 05/34: hibernate7: spec for GrailsSessionContext and InstanceApiHelper
This is an automated email from the ASF dual-hosted git repository.
borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 2b4b3b4edcbb4f2fef1751e95f5d70258c81cc09
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Thu Mar 5 17:57:21 2026 -0600
hibernate7: spec for GrailsSessionContext and InstanceApiHelper
---
.../grails/orm/hibernate/GrailsSessionContext.java | 9 +-
.../support/HibernateDatastoreFactoryBean.groovy | 62 -------
.../HibernateDialectDetectorFactoryBean.java | 204 ---------------------
.../sessioncontext/GrailsSessionContextSpec.groovy | 126 +++++++++++--
.../orm/hibernate/InstanceApiHelperSpec.groovy | 71 +++++++
5 files changed, 183 insertions(+), 289 deletions(-)
diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/GrailsSessionContext.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/GrailsSessionContext.java
index 96bedd079e..e72a03ea45 100644
--- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/GrailsSessionContext.java
+++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/GrailsSessionContext.java
@@ -70,7 +70,7 @@ public class GrailsSessionContext implements CurrentSessionContext {
public void initJta() {
JtaPlatform jtaPlatform = sessionFactory.getServiceRegistry().getService(JtaPlatform.class);
- TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager();
+ TransactionManager transactionManager = jtaPlatform != null ? jtaPlatform.retrieveTransactionManager() : null;
jtaSessionContext =
transactionManager == null ? null : new SpringJtaSessionContext(sessionFactory);
}
@@ -82,9 +82,8 @@ public class GrailsSessionContext implements CurrentSessionContext {
return (Session) value;
}
- if (value instanceof SessionHolder) {
- SessionHolder sessionHolder = (SessionHolder) value;
- Session session = sessionHolder.getSession();
+ if (value instanceof SessionHolder sessionHolder) {
+ Session session = sessionHolder.getSession();
if (TransactionSynchronizationManager.isSynchronizationActive()
&& !sessionHolder.isSynchronizedWithTransaction()) {
TransactionSynchronizationManager.registerSynchronization(
@@ -221,7 +220,7 @@ public class GrailsSessionContext implements CurrentSessionContext {
ServiceBinding<JtaPlatform> sb =
sessionFactoryImpl.getServiceRegistry().locateServiceBinding(JtaPlatform.class);
- if (sb == null) {
+ if (sb == null || sb.getService() == null) {
return null;
}
diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateDatastoreFactoryBean.groovy b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateDatastoreFactoryBean.groovy
deleted file mode 100644
index 2c981dd653..0000000000
--- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateDatastoreFactoryBean.groovy
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.grails.orm.hibernate.support
-
-import groovy.transform.CompileStatic
-import org.grails.datastore.mapping.model.MappingContext
-import org.grails.orm.hibernate.HibernateDatastore
-import org.hibernate.SessionFactory
-import org.springframework.beans.BeansException
-import org.springframework.beans.factory.FactoryBean
-import org.springframework.context.ApplicationContext
-import org.springframework.context.ApplicationContextAware
-import org.springframework.core.env.PropertyResolver
-/**
- * Helper for constructing the datastore
- *
- * @author Graeme Rocher
- * @since 5.0
- */
-@CompileStatic
-public class HibernateDatastoreFactoryBean<T extends HibernateDatastore> implements FactoryBean<T>, ApplicationContextAware {
-
- private final Class<T> objectType;
- private final MappingContext mappingContext;
- private final SessionFactory sessionFactory;
- private final PropertyResolver configuration;
- private final String dataSourceName;
- ApplicationContext applicationContext;
-
- HibernateDatastoreFactoryBean(Class<T> objectType, MappingContext mappingContext, SessionFactory sessionFactory, PropertyResolver configuration, String dataSourceName) {
- this.objectType = objectType
- this.mappingContext = mappingContext
- this.sessionFactory = sessionFactory
- this.configuration = configuration
- this.dataSourceName = dataSourceName
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- @Override
- public T getObject() throws Exception {
- HibernateDatastore datastore = objectType.newInstance(mappingContext, sessionFactory, configuration, dataSourceName)
-
-
- if(applicationContext != null) {
- datastore.setApplicationContext(applicationContext)
- }
-
- return datastore;
- }
-
- @Override
- public Class<?> getObjectType() {
- return objectType;
- }
-
- @Override
- public boolean isSingleton() {
- return true;
- }
-}
diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateDialectDetectorFactoryBean.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateDialectDetectorFactoryBean.java
deleted file mode 100644
index 4ca7317583..0000000000
--- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateDialectDetectorFactoryBean.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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.orm.hibernate.support;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import javax.sql.DataSource;
-import org.checkerframework.checker.initialization.qual.Initialized;
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.checkerframework.checker.nullness.qual.Nullable;
-import org.checkerframework.checker.nullness.qual.UnknownKeyFor;
-import org.grails.orm.hibernate.exceptions.CouldNotDetermineHibernateDialectException;
-import org.hibernate.HibernateException;
-import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
-import org.hibernate.boot.registry.selector.internal.StrategySelectorImpl;
-import org.hibernate.boot.registry.selector.spi.StrategySelector;
-import org.hibernate.dialect.Dialect;
-import org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl;
-import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
-import org.hibernate.engine.jdbc.dialect.spi.*;
-import org.hibernate.service.Service;
-import org.hibernate.service.ServiceRegistry;
-import org.hibernate.service.spi.ServiceBinding;
-import org.hibernate.service.spi.ServiceRegistryImplementor;
-import org.springframework.beans.factory.FactoryBean;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.jdbc.datasource.DataSourceUtils;
-import org.springframework.jdbc.support.JdbcUtils;
-import org.springframework.jdbc.support.MetaDataAccessException;
-import org.springframework.util.Assert;
-import org.springframework.util.StringUtils;
-
-/**
- * @author Steven Devijver
- * @author Graeme Rocher
- * @author Burt Beckwith
- */
-@SuppressWarnings({"PMD.CloseResource", "PMD.DataflowAnomalyAnalysis"})
-public class HibernateDialectDetectorFactoryBean implements FactoryBean<String>, InitializingBean {
-
- private DataSource dataSource;
- private Properties vendorNameDialectMappings;
- private String hibernateDialectClassName;
- private Dialect hibernateDialect;
- private Properties hibernateProperties = new Properties();
-
- public void setHibernateProperties(Properties hibernateProperties) {
- this.hibernateProperties = hibernateProperties;
- }
-
- public void setDataSource(DataSource dataSource) {
- this.dataSource = dataSource;
- }
-
- public void setVendorNameDialectMappings(Properties mappings) {
- vendorNameDialectMappings = mappings;
- }
-
- public String getObject() {
- return hibernateDialectClassName;
- }
-
- public Class<String> getObjectType() {
- return String.class;
- }
-
- public boolean isSingleton() {
- return true;
- }
-
- public void afterPropertiesSet() throws MetaDataAccessException {
- Assert.notNull(dataSource, "Data source is not set!");
- Assert.notNull(vendorNameDialectMappings, "Vendor name/dialect mappings are not set!");
-
- Connection connection = null;
-
- String dbName =
- (String) JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductName");
-
- try {
- connection = DataSourceUtils.getConnection(dataSource);
-
- try {
- final DialectFactory dialectFactory = createDialectFactory();
- final Connection finalConnection = connection;
- DialectResolutionInfoSource infoSource =
- new DialectResolutionInfoSource() {
- @Override
- public DialectResolutionInfo getDialectResolutionInfo() {
- try {
- return new DatabaseMetaDataDialectResolutionInfoAdapter(
- finalConnection.getMetaData());
- } catch (SQLException e) {
- throw new CouldNotDetermineHibernateDialectException(
- "Could not determine Hibernate dialect", e);
- }
- }
- };
- HashMap<String, Object> collect =
- hibernateProperties.entrySet().stream()
- .collect(
- Collectors.toMap(
- e -> String.valueOf(e.getKey()),
- Map.Entry::getValue,
- (prev, next) -> next,
- HashMap::new));
- hibernateDialect = dialectFactory.buildDialect(collect, infoSource);
- hibernateDialectClassName = hibernateDialect.getClass().getName();
- } catch (HibernateException e) {
- hibernateDialectClassName = vendorNameDialectMappings.getProperty(dbName);
- }
-
- if (!StringUtils.hasText(hibernateDialectClassName)) {
- throw new CouldNotDetermineHibernateDialectException(
- "Could not determine Hibernate dialect for database name [" + dbName + "]!");
- }
- } finally {
- DataSourceUtils.releaseConnection(connection, dataSource);
- }
- }
-
- // should be using the ServiceRegistry, but getting it from the SessionFactory at startup fails in
- // Spring
- protected DialectFactory createDialectFactory() {
- DialectFactoryImpl factory = new DialectFactoryImpl();
- factory.injectServices(
- new ServiceRegistryImplementor() {
-
- @Override
- public <R extends Service> R getService(Class<R> serviceRole) {
- if (serviceRole == DialectResolver.class) {
- return (R) new StandardDialectResolver();
- } else if (serviceRole == StrategySelector.class) {
- return (R)
- new StrategySelectorImpl(
- new ClassLoaderServiceImpl(Thread.currentThread().getContextClassLoader()));
- }
- return null;
- }
-
- @Override
- public <R extends Service> R requireService(
- @UnknownKeyFor @NonNull @Initialized Class<R> serviceRole) {
- return ServiceRegistryImplementor.super.requireService(serviceRole);
- }
-
- @Override
- public <R extends Service> ServiceBinding<R> locateServiceBinding(Class<R> serviceRole) {
- return null;
- }
-
- @Override
- public void close() {
- ServiceRegistryImplementor.super.close();
- }
-
- @Override
- public void destroy() {}
-
- @Override
- public void registerChild(ServiceRegistryImplementor child) {}
-
- @Override
- public void deRegisterChild(ServiceRegistryImplementor child) {}
-
- @Override
- public <T extends Service> @Nullable T fromRegistryOrChildren(
- @UnknownKeyFor @NonNull @Initialized Class<T> serviceRole) {
- return null;
- }
-
- @Override
- public ServiceRegistry getParentServiceRegistry() {
- return null;
- }
-
- @Override
- public boolean isActive() {
- return true;
- }
- });
- return factory;
- }
-}
diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/sessioncontext/GrailsSessionContextSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/sessioncontext/GrailsSessionContextSpec.groovy
index d9d1461d48..4534c4f506 100644
--- a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/sessioncontext/GrailsSessionContextSpec.groovy
+++ b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/sessioncontext/GrailsSessionContextSpec.groovy
@@ -1,3 +1,21 @@
+/*
+ * 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.gorm.specs.sessioncontext
import grails.gorm.specs.HibernateGormDatastoreSpec
@@ -11,6 +29,20 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
class GrailsSessionContextSpec extends HibernateGormDatastoreSpec {
+ def setup() {
+ TransactionSynchronizationManager.unbindResourceIfPossible(manager.hibernateDatastore.sessionFactory)
+ if (TransactionSynchronizationManager.isSynchronizationActive()) {
+ TransactionSynchronizationManager.clearSynchronization()
+ }
+ }
+
+ def cleanup() {
+ TransactionSynchronizationManager.unbindResourceIfPossible(manager.hibernateDatastore.sessionFactory)
+ if (TransactionSynchronizationManager.isSynchronizationActive()) {
+ TransactionSynchronizationManager.clearSynchronization()
+ }
+ }
+
void "test GrailsSessionContext can be created with a SessionFactory"() {
given:
HibernateDatastore hibernateDatastore = manager.hibernateDatastore
@@ -25,12 +57,9 @@ class GrailsSessionContextSpec extends HibernateGormDatastoreSpec {
void "test currentSession() returns session bound via TransactionSynchronizationManager"() {
given:
- HibernateDatastore hibernateDatastore = manager.hibernateDatastore
- SessionFactoryImplementor sessionFactory = hibernateDatastore.sessionFactory as SessionFactoryImplementor
+ SessionFactoryImplementor sessionFactory = manager.hibernateDatastore.sessionFactory as SessionFactoryImplementor
GrailsSessionContext sessionContext = new GrailsSessionContext(sessionFactory)
Session session = sessionFactory.openSession()
- // unbind whatever the test framework bound, then bind our own session
- TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory)
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session))
when:
@@ -41,36 +70,26 @@ class GrailsSessionContextSpec extends HibernateGormDatastoreSpec {
current == session
cleanup:
- TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory)
if (session.isOpen()) session.close()
}
void "test currentSession() throws when no session is bound and allowCreate is false"() {
given:
- HibernateDatastore hibernateDatastore = manager.hibernateDatastore
- SessionFactoryImplementor sessionFactory = hibernateDatastore.sessionFactory as SessionFactoryImplementor
+ SessionFactoryImplementor sessionFactory = manager.hibernateDatastore.sessionFactory as SessionFactoryImplementor
GrailsSessionContext sessionContext = new GrailsSessionContext(sessionFactory)
- // unbind whatever the test framework bound
- def saved = TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory)
when:
sessionContext.currentSession()
then:
thrown(org.hibernate.HibernateException)
-
- cleanup:
- // restore the original binding so the framework is not broken for subsequent tests
- if (saved) TransactionSynchronizationManager.bindResource(sessionFactory, saved)
}
void "test currentSession() returns session when bound as plain Session resource"() {
given:
- HibernateDatastore hibernateDatastore = manager.hibernateDatastore
- SessionFactoryImplementor sessionFactory = hibernateDatastore.sessionFactory as SessionFactoryImplementor
+ SessionFactoryImplementor sessionFactory = manager.hibernateDatastore.sessionFactory as SessionFactoryImplementor
GrailsSessionContext sessionContext = new GrailsSessionContext(sessionFactory)
Session session = sessionFactory.openSession()
- TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory)
TransactionSynchronizationManager.bindResource(sessionFactory, session)
when:
@@ -80,8 +99,79 @@ class GrailsSessionContextSpec extends HibernateGormDatastoreSpec {
current == session
cleanup:
- TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory)
if (session.isOpen()) session.close()
}
-}
+ void "test initJta handles missing JtaPlatform"() {
+ given:
+ SessionFactoryImplementor sessionFactory = Mock(SessionFactoryImplementor)
+ org.hibernate.service.spi.ServiceRegistryImplementor registry = Mock(org.hibernate.service.spi.ServiceRegistryImplementor)
+ sessionFactory.getServiceRegistry() >> registry
+ registry.getService(org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform) >> null
+ GrailsSessionContext sessionContext = new GrailsSessionContext(sessionFactory)
+
+ when:
+ sessionContext.initJta()
+
+ then:
+ noExceptionThrown()
+ sessionContext.jtaSessionContext == null
+ }
+
+ void "test currentSession() switches to AUTO flush mode when sync is active"() {
+ given:
+ SessionFactoryImplementor sessionFactory = manager.hibernateDatastore.sessionFactory as SessionFactoryImplementor
+ GrailsSessionContext sessionContext = new GrailsSessionContext(sessionFactory)
+ Session session = sessionFactory.openSession()
+ session.setHibernateFlushMode(FlushMode.MANUAL)
+
+ TransactionSynchronizationManager.initSynchronization()
+ TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session))
+
+ when:
+ Session current = sessionContext.currentSession()
+
+ then:
+ current.getHibernateFlushMode() == FlushMode.AUTO
+
+ cleanup:
+ if (session.isOpen()) session.close()
+ }
+
+ void "test currentSession() creates a new session when allowCreate is true"() {
+ given:
+ SessionFactoryImplementor sessionFactory = manager.hibernateDatastore.sessionFactory as SessionFactoryImplementor
+ GrailsSessionContext sessionContext = new GrailsSessionContext(sessionFactory)
+ sessionContext.allowCreate = true
+
+ when:
+ Session session = sessionContext.currentSession()
+
+ then:
+ session != null
+ session.isOpen()
+
+ cleanup:
+ if (session?.isOpen()) session.close()
+ }
+
+ void "test currentSession() with active transaction and allowCreate"() {
+ given:
+ SessionFactoryImplementor sessionFactory = manager.hibernateDatastore.sessionFactory as SessionFactoryImplementor
+ GrailsSessionContext sessionContext = new GrailsSessionContext(sessionFactory)
+ sessionContext.allowCreate = true
+
+ TransactionSynchronizationManager.initSynchronization()
+
+ when:
+ Session session = sessionContext.currentSession()
+
+ then:
+ session != null
+ TransactionSynchronizationManager.hasResource(sessionFactory)
+ ((SessionHolder)TransactionSynchronizationManager.getResource(sessionFactory)).isSynchronizedWithTransaction()
+
+ cleanup:
+ if (session?.isOpen()) session.close()
+ }
+}
diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/InstanceApiHelperSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/InstanceApiHelperSpec.groovy
new file mode 100644
index 0000000000..009e04cd0d
--- /dev/null
+++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/InstanceApiHelperSpec.groovy
@@ -0,0 +1,71 @@
+/*
+ * 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.orm.hibernate
+
+import org.hibernate.FlushMode
+import org.hibernate.Session
+import spock.lang.Specification
+
+class InstanceApiHelperSpec extends Specification {
+
+ GrailsHibernateTemplate template = Mock(GrailsHibernateTemplate)
+ Session session = Mock(Session)
+ InstanceApiHelper helper = new InstanceApiHelper(template)
+
+ def "test remove without flush"() {
+ given:
+ def obj = new Object()
+
+ when:
+ helper.remove(obj, false)
+
+ then:
+ 1 * template.execute(_) >> { args ->
+ args[0].doInHibernate(session)
+ }
+ 1 * session.remove(obj)
+ 0 * session.flush()
+ }
+
+ def "test remove with flush"() {
+ given:
+ def obj = new Object()
+
+ when:
+ helper.remove(obj, true)
+
+ then:
+ 1 * template.execute(_) >> { args ->
+ args[0].doInHibernate(session)
+ }
+ 1 * session.remove(obj)
+ 1 * session.flush()
+ }
+
+ def "test setFlushModeManual"() {
+ when:
+ helper.setFlushModeManual()
+
+ then:
+ 1 * template.execute(_) >> { args ->
+ args[0].doInHibernate(session)
+ }
+ 1 * session.setHibernateFlushMode(FlushMode.MANUAL)
+ }
+}