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 <[email protected]> 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) + } +}
