This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 722b564bf6b6b148e5e9e4dfe9642e718d814c4e Author: Walter B Duque de Estrada <[email protected]> AuthorDate: Thu Jan 22 12:47:46 2026 -0600 update progress --- .../orm/hibernate/GrailsHibernateTemplate.java | 2 +- .../orm/hibernate/proxy/HibernateProxyHandler.java | 50 +++++++- .../gorm/specs/HibernateGormDatastoreSpec.groovy | 4 +- .../proxy/HibernateProxyHandlerSpec.groovy | 3 + .../orm/hibernate/GrailsHibernateTemplate.java | 2 +- .../orm/hibernate/proxy/HibernateProxyHandler.java | 51 +++++++- .../gorm/specs/HibernateGormDatastoreSpec.groovy | 13 +- .../proxy/HibernateProxyHandlerSpec.groovy | 19 ++- .../gorm/specs/HibernateGormDatastoreSpec.groovy | 139 --------------------- .../core/GrailsDataHibernate5TckManager.groovy | 132 ------------------- .../proxy/HibernateProxyHandlerSpec.groovy | 73 ----------- .../gorm/specs/HibernateGormDatastoreSpec.groovy | 139 --------------------- .../core/GrailsDataHibernate6TckManager.groovy | 132 ------------------- .../proxy/HibernateProxyHandlerSpec.groovy | 73 ----------- .../proxy/HibernateProxyHandlerSpec.groovy | 73 ----------- 15 files changed, 126 insertions(+), 779 deletions(-) diff --git a/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java index 84366ff921..1a291d502b 100644 --- a/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java +++ b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java @@ -342,7 +342,7 @@ public class GrailsHibernateTemplate implements IHibernateTemplate { return sessionHolder != null && sessionHolder.getSession() == session; } - protected Session getSession() { + public Session getSession() { try { return sessionFactory.getCurrentSession(); } catch (HibernateException ex) { diff --git a/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java index 6a802bddc1..bb5921e82a 100644 --- a/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java +++ b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java @@ -20,17 +20,22 @@ package org.grails.orm.hibernate.proxy; import java.io.Serializable; +import groovy.lang.GroovyObject; +import groovy.lang.MetaClass; +import org.codehaus.groovy.runtime.HandleMetaClass; import org.hibernate.Hibernate; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxyHelper; +import org.grails.datastore.gorm.proxy.ProxyInstanceMetaClass; import org.grails.datastore.mapping.core.Session; import org.grails.datastore.mapping.engine.AssociationQueryExecutor; import org.grails.datastore.mapping.proxy.EntityProxy; import org.grails.datastore.mapping.proxy.ProxyFactory; import org.grails.datastore.mapping.proxy.ProxyHandler; import org.grails.datastore.mapping.reflect.ClassPropertyFetcher; +import org.grails.orm.hibernate.GrailsHibernateTemplate; /** * Implementation of the ProxyHandler interface for Hibernate using org.hibernate.Hibernate @@ -50,6 +55,10 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { if (o instanceof EntityProxy) { return ((EntityProxy)o).isInitialized(); } + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(o); + if (proxyMc != null) { + return proxyMc.isProxyInitiated(); + } return Hibernate.isInitialized(o); } @@ -79,6 +88,10 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { if (object instanceof EntityProxy) { return ((EntityProxy)object).getTarget(); } + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(object); + if (proxyMc != null) { + return proxyMc.getProxyTarget(); + } if (object instanceof PersistentCollection) { initialize(object); return object; @@ -95,6 +108,10 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { if (o instanceof EntityProxy) { return ((EntityProxy)o).getProxyKey(); } + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(o); + if (proxyMc != null) { + return proxyMc.getKey(); + } if (o instanceof HibernateProxy) { return (Serializable) ((HibernateProxy)o).getHibernateLazyInitializer().getIdentifier(); } @@ -127,6 +144,9 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { */ @Override public boolean isProxy(Object o) { + if (getProxyInstanceMetaClass(o) != null) { + return true; + } return (o instanceof EntityProxy) || (o instanceof HibernateProxy) || (o instanceof PersistentCollection); } @@ -140,13 +160,39 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { ((EntityProxy)o).initialize(); } else { - Hibernate.initialize(o); + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(o); + if (proxyMc != null) { + proxyMc.getProxyTarget(); + } + else { + Hibernate.initialize(o); + } + } + } + + private ProxyInstanceMetaClass getProxyInstanceMetaClass(Object o) { + if (o instanceof GroovyObject) { + MetaClass mc = ((GroovyObject) o).getMetaClass(); + if (mc instanceof HandleMetaClass) { + mc = ((HandleMetaClass) mc).getAdaptee(); + } + if (mc instanceof ProxyInstanceMetaClass) { + return (ProxyInstanceMetaClass) mc; + } } + return null; } @Override public <T> T createProxy(Session session, Class<T> type, Serializable key) { - throw new UnsupportedOperationException("createProxy not supported in HibernateProxyHandler"); + org.hibernate.Session hibSession = null; + if (session.getNativeInterface() instanceof GrailsHibernateTemplate grailsHibernateTemplate) { + hibSession = grailsHibernateTemplate.getSession(); + } + if (hibSession == null) { + throw new IllegalStateException("Could not obtain native Hibernate Session from Session#getNativeInterface()"); + } + return (T) hibSession.getReference(type, key); } @Override diff --git a/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy b/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy index f0fb74d50d..1e398d172a 100644 --- a/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy +++ b/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy @@ -65,7 +65,7 @@ class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate5T ${staticMapping.collect { name, value -> "${name} ${value}" }.join('\n ')} } } - """.stripIndent() + """ def clazz = classLoader.parseClass(classText) createPersistentEntity(clazz, binder) @@ -136,4 +136,4 @@ class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate5T protected HibernateQuery getQuery(Class clazz) { return new HibernateQuery(session, getPersistentEntity(clazz)) } -} +} \ No newline at end of file diff --git a/grails-data-hibernate5/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy b/grails-data-hibernate5/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy index 1b34fe9b1e..ce09bc546e 100644 --- a/grails-data-hibernate5/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy +++ b/grails-data-hibernate5/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy @@ -30,6 +30,9 @@ class HibernateProxyHandlerSpec extends HibernateGormDatastoreSpec { // Get a proxy without initializing it Location proxyLocation = Location.proxy(location.id) + println "proxyLocation class: ${proxyLocation.getClass().name}" + println "proxyLocation instanceof EntityProxy: ${proxyLocation instanceof org.grails.datastore.mapping.proxy.EntityProxy}" + println "Hibernate.isInitialized(proxyLocation): ${org.hibernate.Hibernate.isInitialized(proxyLocation)}" expect: proxyHandler.isInitialized(proxyLocation) == false diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java index 8268a0d1a8..41300515db 100644 --- a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/GrailsHibernateTemplate.java @@ -322,7 +322,7 @@ public class GrailsHibernateTemplate implements IHibernateTemplate { return sessionHolder != null && sessionHolder.getSession() == session; } - protected Session getSession() { + public Session getSession() { try { return sessionFactory.getCurrentSession(); } catch (HibernateException ex) { diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java index 05ef97685a..6c722b8f62 100644 --- a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java @@ -28,6 +28,12 @@ import org.hibernate.proxy.HibernateProxyHelper; import java.io.Serializable; +import groovy.lang.GroovyObject; +import groovy.lang.MetaClass; +import org.codehaus.groovy.runtime.HandleMetaClass; +import org.grails.datastore.gorm.proxy.ProxyInstanceMetaClass; +import org.grails.orm.hibernate.GrailsHibernateTemplate; + /** * Implementation of the ProxyHandler interface for Hibernate using org.hibernate.Hibernate * and HibernateProxyHelper where possible. @@ -46,6 +52,10 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { if (o instanceof EntityProxy) { return ((EntityProxy)o).isInitialized(); } + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(o); + if (proxyMc != null) { + return proxyMc.isProxyInitiated(); + } return Hibernate.isInitialized(o); } @@ -75,6 +85,10 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { if (object instanceof EntityProxy) { return ((EntityProxy)object).getTarget(); } + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(object); + if (proxyMc != null) { + return proxyMc.getProxyTarget(); + } if (object instanceof PersistentCollection) { initialize(object); return object; @@ -91,6 +105,10 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { if (o instanceof EntityProxy) { return ((EntityProxy)o).getProxyKey(); } + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(o); + if (proxyMc != null) { + return proxyMc.getKey(); + } if (o instanceof HibernateProxy) { return (Serializable) ((HibernateProxy)o).getHibernateLazyInitializer().getIdentifier(); } @@ -123,6 +141,9 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { */ @Override public boolean isProxy(Object o) { + if (getProxyInstanceMetaClass(o) != null) { + return true; + } return (o instanceof EntityProxy) || (o instanceof HibernateProxy) || (o instanceof PersistentCollection); } @@ -136,13 +157,39 @@ public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { ((EntityProxy)o).initialize(); } else { - Hibernate.initialize(o); + ProxyInstanceMetaClass proxyMc = getProxyInstanceMetaClass(o); + if (proxyMc != null) { + proxyMc.getProxyTarget(); + } + else { + Hibernate.initialize(o); + } } } + private ProxyInstanceMetaClass getProxyInstanceMetaClass(Object o) { + if (o instanceof GroovyObject) { + MetaClass mc = ((GroovyObject) o).getMetaClass(); + if (mc instanceof HandleMetaClass) { + mc = ((HandleMetaClass) mc).getAdaptee(); + } + if (mc instanceof ProxyInstanceMetaClass) { + return (ProxyInstanceMetaClass) mc; + } + } + return null; + } + @Override public <T> T createProxy(Session session, Class<T> type, Serializable key) { - throw new UnsupportedOperationException("createProxy not supported in HibernateProxyHandler"); + org.hibernate.Session hibSession = null; + if (session.getNativeInterface() instanceof GrailsHibernateTemplate grailsHibernateTemplate) { + hibSession = grailsHibernateTemplate.getSession(); + } + if (hibSession == null) { + throw new IllegalStateException("Could not obtain native Hibernate Session from Session#getNativeInterface()"); + } + return (T) hibSession.getReference(type, key); } @Override diff --git a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy index 5055c286a2..19c3d784f6 100644 --- a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy +++ b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy @@ -2,7 +2,6 @@ package grails.gorm.specs import org.apache.grails.data.hibernate6.core.GrailsDataHibernate6TckManager import org.apache.grails.data.testing.tck.base.GrailsDataTckSpec -import org.apache.grails.data.testing.tck.domains.Person import org.grails.datastore.mapping.model.PersistentEntity import org.grails.orm.hibernate.AbstractHibernateSession import org.grails.orm.hibernate.HibernateDatastore @@ -10,7 +9,7 @@ import org.grails.orm.hibernate.cfg.GrailsDomainBinder import org.grails.orm.hibernate.cfg.HibernateMappingContext import org.grails.orm.hibernate.cfg.HibernatePersistentEntity import org.grails.orm.hibernate.query.HibernateQuery -import org.hibernate.Session + import org.hibernate.boot.MetadataSources import org.hibernate.boot.internal.BootstrapContextImpl import org.hibernate.boot.internal.InFlightMetadataCollectorImpl @@ -18,10 +17,10 @@ import org.hibernate.boot.internal.MetadataBuilderImpl import org.hibernate.boot.registry.BootstrapServiceRegistry import org.hibernate.boot.registry.StandardServiceRegistryBuilder import org.hibernate.boot.registry.classloading.spi.ClassLoaderService -import org.hibernate.boot.spi.MetadataContributor import org.hibernate.dialect.H2Dialect import org.hibernate.internal.SessionFactoryImpl import org.hibernate.service.spi.ServiceRegistryImplementor +import org.hibernate.boot.spi.MetadataContributor /** * The original GormDataStoreSpec destroyed the setup @@ -113,10 +112,14 @@ class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate6T } protected ServiceRegistryImplementor getServiceRegistry() { - (manager.hibernateDatastore.sessionFactory as SessionFactoryImpl) + getSessionFactory() .getServiceRegistry() } + protected SessionFactoryImpl getSessionFactory() { + manager.hibernateDatastore.sessionFactory as SessionFactoryImpl + } + protected HibernateDatastore getDatastore() { manager.hibernateDatastore } @@ -133,4 +136,4 @@ class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate6T protected HibernateQuery getQuery(Class clazz) { return new HibernateQuery(session, getPersistentEntity(clazz)) } -} +} \ No newline at end of file diff --git a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy index be9c987ff9..ceaf3683a3 100644 --- a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy +++ b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy @@ -29,10 +29,10 @@ class HibernateProxyHandlerSpec extends HibernateGormDatastoreSpec { manager.hibernateSession.clear() // Get a proxy without initializing it - println "Calling getReference..." - Location proxyLocation = manager.hibernateSession.getReference(Location, location.id) - println "Got proxy: ${proxyLocation.getClass().name}" - println "Is Initialized (Hibernate): ${Hibernate.isInitialized(proxyLocation)}" + Location proxyLocation = Location.proxy(location.id) + println "proxyLocation class: ${proxyLocation.getClass().name}" + println "proxyLocation instanceof EntityProxy: ${proxyLocation instanceof org.grails.datastore.mapping.proxy.EntityProxy}" + println "Hibernate.isInitialized(proxyLocation): ${org.hibernate.Hibernate.isInitialized(proxyLocation)}" expect: proxyHandler.isInitialized(proxyLocation) == false @@ -63,6 +63,15 @@ class HibernateProxyHandlerSpec extends HibernateGormDatastoreSpec { // Get a proxy without initializing it Location proxyLocation = Location.proxy(location.id) + println "Groovy proxyLocation class: ${proxyLocation.getClass().name}" + println "Groovy proxyLocation instanceof GroovyObject: ${proxyLocation instanceof GroovyObject}" + println "Groovy proxyLocation MetaClass: ${proxyLocation.getMetaClass().getClass().name}" + if (proxyLocation instanceof GroovyObject) { + def mc = proxyLocation.getMetaClass() + if (mc instanceof org.codehaus.groovy.runtime.HandleMetaClass) { + println "Groovy proxyLocation Adaptee: ${mc.getAdaptee().getClass().name}" + } + } expect: proxyHandler.isInitialized(proxyLocation) == false @@ -103,4 +112,4 @@ class HibernateProxyHandlerSpec extends HibernateGormDatastoreSpec { cleanup: manager.session.mappingContext.proxyFactory = originalFactory } -} \ No newline at end of file +} diff --git a/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy b/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy deleted file mode 100644 index c090fbda24..0000000000 --- a/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy +++ /dev/null @@ -1,139 +0,0 @@ -package grails.gorm.specs - -import org.apache.grails.data.hibernate5.core.GrailsDataHibernate5TckManager // Changed -import org.apache.grails.data.testing.tck.base.GrailsDataTckSpec -import org.grails.datastore.mapping.model.PersistentEntity -import org.grails.orm.hibernate.AbstractHibernateSession -import org.grails.orm.hibernate.HibernateDatastore -import org.grails.orm.hibernate.cfg.GrailsDomainBinder -import org.grails.orm.hibernate.cfg.HibernateMappingContext -import org.grails.orm.hibernate.cfg.HibernatePersistentEntity -import org.grails.orm.hibernate.query.HibernateQuery - -import org.hibernate.boot.MetadataSources -import org.hibernate.boot.internal.BootstrapContextImpl -import org.hibernate.boot.internal.InFlightMetadataCollectorImpl -import org.hibernate.boot.internal.MetadataBuilderImpl -import org.hibernate.boot.registry.BootstrapServiceRegistry -import org.hibernate.boot.registry.StandardServiceRegistryBuilder -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService -import org.hibernate.dialect.H2Dialect -import org.hibernate.internal.SessionFactoryImpl // Assuming compatible for H5 -import org.hibernate.service.spi.ServiceRegistryImplementor -import org.hibernate.boot.spi.AdditionalMappingContributor - -/** - * The original GormDataStoreSpec destroyed the setup - * between tests instead of at the end of all tests - * It also wqs default configured for H2 which - * made it break with some Java types. - * Finally, it loaded all the test Entities, - * now it can be setup individually. - */ -class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate5TckManager> { // Changed - - void setupSpec() { - manager.grailsConfig = [ - 'dataSource.url' : "jdbc:h2:mem:grailsDB;LOCK_TIMEOUT=10000", - 'dataSource.dbCreate' : 'create-drop', - 'dataSource.formatSql' : 'true', - 'dataSource.logSql' : 'true', - 'hibernate.flush.mode' : 'COMMIT', - 'hibernate.cache.queries' : 'true', - 'hibernate.hbm2ddl.auto' : 'create', - 'hibernate.jpa.compliance.cascade': 'true', - ] - } - - HibernatePersistentEntity createPersistentEntity(GrailsDomainBinder binder - , String className - , Map<String, Class> fieldProperties - , Map<String, String> staticMapping - - ) { - def classLoader = new GroovyClassLoader() - def classText = """ - package foo - import grails.gorm.annotation.Entity - import grails.gorm.hibernate.HibernateEntity - @Entity - class ${className} implements HibernateEntity<${className}> { - - ${fieldProperties.collect { name, type -> "${type.simpleName} ${name}" }.join('\n ')} - - static mapping = { - ${staticMapping.collect { name, value -> "${name} ${value}" }.join('\n ')} - } - } - """ - - def clazz = classLoader.parseClass(classText) - createPersistentEntity(clazz, binder) - } - - HibernatePersistentEntity createPersistentEntity(Class clazz, GrailsDomainBinder binder) { - def entity = getMappingContext().addPersistentEntity(clazz) as HibernatePersistentEntity - binder.evaluateMapping(entity) - entity - } - - HibernatePersistentEntity createPersistentEntity(Class clazz) { - return createPersistentEntity(clazz, getGrailsDomainBinder()) - } - - protected InFlightMetadataCollectorImpl getCollector() { - def bootstrapServiceRegistry = getServiceRegistry() - .getParentServiceRegistry() - .getParentServiceRegistry() as BootstrapServiceRegistry - def serviceRegistry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry) - .applySetting("hibernate.dialect", H2Dialect.class.getName()) - .applySetting("jakarta.persistence.jdbc.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") - .applySetting("jakarta.persistence.jdbc.driver", "org.h2.Driver") - .build() - def options = new MetadataBuilderImpl( - new MetadataSources(serviceRegistry) - ).getMetadataBuildingOptions() - new InFlightMetadataCollectorImpl( - new BootstrapContextImpl( serviceRegistry, options) - , options); - } - - protected HibernateMappingContext getMappingContext() { - manager.hibernateDatastore.getMappingContext() - } - - protected GrailsDomainBinder getGrailsDomainBinder() { - def registry = getServiceRegistry() - registry - .getParentServiceRegistry() - .getService(ClassLoaderService.class) - .loadJavaServices(AdditionalMappingContributor.class) - .find { it instanceof GrailsDomainBinder } - } - - protected ServiceRegistryImplementor getServiceRegistry() { - getSessionFactory() - .getServiceRegistry() - } - - protected SessionFactoryImpl getSessionFactory() { - manager.hibernateDatastore.sessionFactory as SessionFactoryImpl - } - - protected HibernateDatastore getDatastore() { - manager.hibernateDatastore - } - - - protected AbstractHibernateSession getSession() { - datastore.connect() as AbstractHibernateSession - } - - protected PersistentEntity getPersistentEntity(Class clazz) { - getMappingContext().getPersistentEntity(clazz.typeName) - } - - protected HibernateQuery getQuery(Class clazz) { - return new HibernateQuery(session, getPersistentEntity(clazz)) - } -} diff --git a/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/org/apache/grails/data/hibernate5/core/GrailsDataHibernate5TckManager.groovy b/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/org/apache/grails/data/hibernate5/core/GrailsDataHibernate5TckManager.groovy deleted file mode 100644 index 2dba11fb69..0000000000 --- a/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/org/apache/grails/data/hibernate5/core/GrailsDataHibernate5TckManager.groovy +++ /dev/null @@ -1,132 +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.apache.grails.data.hibernate5.core - -import grails.core.DefaultGrailsApplication -import grails.core.GrailsApplication -import groovy.sql.Sql -import org.apache.grails.data.testing.tck.base.GrailsDataTckManager -import org.grails.datastore.mapping.core.DatastoreUtils -import org.grails.datastore.mapping.core.Session -import org.grails.orm.hibernate.GrailsHibernateTransactionManager -import org.grails.orm.hibernate.HibernateDatastore -import org.grails.orm.hibernate.cfg.HibernateMappingContextConfiguration -import org.h2.Driver -import org.hibernate.SessionFactory -import org.springframework.beans.factory.DisposableBean -import org.springframework.context.ApplicationContext -import org.springframework.orm.hibernate5.SessionFactoryUtils -import org.springframework.orm.hibernate5.SessionHolder -import org.springframework.transaction.TransactionStatus -import org.springframework.transaction.support.DefaultTransactionDefinition -import org.springframework.transaction.support.TransactionSynchronizationManager -import spock.lang.Specification - -class GrailsDataHibernate5TckManager extends GrailsDataTckManager { - GrailsApplication grailsApplication - HibernateDatastore hibernateDatastore - org.hibernate.Session hibernateSession - GrailsHibernateTransactionManager transactionManager - SessionFactory sessionFactory - TransactionStatus transactionStatus - HibernateMappingContextConfiguration hibernateConfig - ApplicationContext applicationContext - ConfigObject grailsConfig = new ConfigObject() - boolean isTransactional = true - - @Override - void setup(Class<? extends Specification> spec) { - cleanRegistry() - super.setup(spec) - } - - @Override - Session createSession() { - System.setProperty('hibernate5.gorm.suite', "true") - grailsApplication = new DefaultGrailsApplication(domainClasses as Class[], new GroovyClassLoader(GrailsDataHibernate5TckManager.getClassLoader())) - grailsConfig.dataSource.dbCreate = "create-drop" - grailsConfig.hibernate.proxy_factory_class = "yakworks.hibernate.proxy.ByteBuddyGroovyProxyFactory" - if (grailsConfig) { - grailsApplication.config.putAll(grailsConfig) - } - hibernateDatastore = new HibernateDatastore(DatastoreUtils.createPropertyResolver(grailsConfig), domainClasses as Class[]) - transactionManager = hibernateDatastore.getTransactionManager() - sessionFactory = hibernateDatastore.sessionFactory - if (transactionStatus == null && isTransactional) { - transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()) - } else if (isTransactional) { - throw new RuntimeException("new transaction started during active transaction") - } - if (!isTransactional) { - hibernateSession = sessionFactory.openSession() - TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(hibernateSession)) - } else { - hibernateSession = sessionFactory.currentSession - } - - return hibernateDatastore.connect() - } - - @Override - void destroy() { - super.destroy() - - if (transactionStatus != null) { - def tx = transactionStatus - transactionStatus = null - transactionManager.rollback(tx) - } - if (hibernateSession != null) { - SessionFactoryUtils.closeSession((org.hibernate.Session) hibernateSession) - } - - if (hibernateConfig != null) { - hibernateConfig = null - } - if (hibernateDatastore != null) { - hibernateDatastore.destroy() - } - grailsApplication = null - hibernateDatastore = null - hibernateSession = null - transactionManager = null - sessionFactory = null - if (applicationContext instanceof DisposableBean) { - applicationContext.destroy() - } - applicationContext = null - shutdownInMemDb() - } - - private void shutdownInMemDb() { - Sql sql = null - try { - sql = Sql.newInstance('jdbc:h2:mem:grailsDb', 'sa', '', Driver.name) - sql.executeUpdate('SHUTDOWN') - } catch (e) { - // already closed, ignore - } finally { - try { - sql?.close() - } catch (ignored) { - } - } - } -} diff --git a/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy b/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy deleted file mode 100644 index 4a1bd67752..0000000000 --- a/grails-data-hibernate7/core/grails-data-hibernate5/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy +++ /dev/null @@ -1,73 +0,0 @@ -package org.grails.orm.hibernate.proxy - -import grails.gorm.annotation.Entity -import grails.gorm.specs.HibernateGormDatastoreSpec -import org.hibernate.Hibernate -import spock.lang.Shared - -class HibernateProxyHandlerSpec extends HibernateGormDatastoreSpec { - - @Shared HibernateProxyHandler proxyHandler = new HibernateProxyHandler() - - void "test isInitialized for a non-proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - - expect: - proxyHandler.isInitialized(location) == true - } - - void "test isInitialized for a proxied object before initialization"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - // Get a proxy without initializing it - Location proxyLocation = Location.load(location.id) - - expect: - proxyHandler.isInitialized(proxyLocation) == false - !Hibernate.isInitialized(proxyLocation) - } - - void "test isInitialized for a proxied object after initialization"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - Location proxyLocation = Location.load(location.id) - proxyLocation.name // Accessing a property to initialize the proxy - - expect: - proxyHandler.isInitialized(proxyLocation) == true - Hibernate.isInitialized(proxyLocation) - } - - void "test unwrap for a non-proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - - expect: - proxyHandler.unwrap(location) == location - } - - void "test unwrap for a proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - Location proxyLocation = Location.load(location.id) - def unwrapped = proxyHandler.unwrap(proxyLocation) - - expect: - unwrapped != proxyLocation - unwrapped.name == location.name // After unwrap, it should be initialized and contain original data - } -} - -@Entity -class Location { - Long id - Long version - String name -} \ No newline at end of file diff --git a/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy b/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy deleted file mode 100644 index c8e72085e9..0000000000 --- a/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/HibernateGormDatastoreSpec.groovy +++ /dev/null @@ -1,139 +0,0 @@ -package grails.gorm.specs - -import org.apache.grails.data.hibernate6.core.GrailsDataHibernate6TckManager // Changed -import org.apache.grails.data.testing.tck.base.GrailsDataTckSpec -import org.grails.datastore.mapping.model.PersistentEntity -import org.grails.orm.hibernate.AbstractHibernateSession -import org.grails.orm.hibernate.HibernateDatastore -import org.grails.orm.hibernate.cfg.GrailsDomainBinder -import org.grails.orm.hibernate.cfg.HibernateMappingContext -import org.grails.orm.hibernate.cfg.HibernatePersistentEntity -import org.grails.orm.hibernate.query.HibernateQuery - -import org.hibernate.boot.MetadataSources -import org.hibernate.boot.internal.BootstrapContextImpl -import org.hibernate.boot.internal.InFlightMetadataCollectorImpl -import org.hibernate.boot.internal.MetadataBuilderImpl -import org.hibernate.boot.registry.BootstrapServiceRegistry -import org.hibernate.boot.registry.StandardServiceRegistryBuilder -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService -import org.hibernate.dialect.H2Dialect -import org.hibernate.internal.SessionFactoryImpl // Assuming compatible for H6 -import org.hibernate.service.spi.ServiceRegistryImplementor -import org.hibernate.boot.spi.AdditionalMappingContributor - -/** - * The original GormDataStoreSpec destroyed the setup - * between tests instead of at the end of all tests - * It also wqs default configured for H2 which - * made it break with some Java types. - * Finally, it loaded all the test Entities, - * now it can be setup individually. - */ -class HibernateGormDatastoreSpec extends GrailsDataTckSpec<GrailsDataHibernate6TckManager> { // Changed - - void setupSpec() { - manager.grailsConfig = [ - 'dataSource.url' : "jdbc:h2:mem:grailsDB;LOCK_TIMEOUT=10000", - 'dataSource.dbCreate' : 'create-drop', - 'dataSource.formatSql' : 'true', - 'dataSource.logSql' : 'true', - 'hibernate.flush.mode' : 'COMMIT', - 'hibernate.cache.queries' : 'true', - 'hibernate.hbm2ddl.auto' : 'create', - 'hibernate.jpa.compliance.cascade': 'true', - ] - } - - HibernatePersistentEntity createPersistentEntity(GrailsDomainBinder binder - , String className - , Map<String, Class> fieldProperties - , Map<String, String> staticMapping - - ) { - def classLoader = new GroovyClassLoader() - def classText = """ - package foo - import grails.gorm.annotation.Entity - import grails.gorm.hibernate.HibernateEntity - @Entity - class ${className} implements HibernateEntity<${className}> { - - ${fieldProperties.collect { name, type -> "${type.simpleName} ${name}" }.join('\n ')} - - static mapping = { - ${staticMapping.collect { name, value -> "${name} ${name}" }.join('\n ')} - } - } - """ - - def clazz = classLoader.parseClass(classText) - createPersistentEntity(clazz, binder) - } - - HibernatePersistentEntity createPersistentEntity(Class clazz, GrailsDomainBinder binder) { - def entity = getMappingContext().addPersistentEntity(clazz) as HibernatePersistentEntity - binder.evaluateMapping(entity) - entity - } - - HibernatePersistentEntity createPersistentEntity(Class clazz) { - return createPersistentEntity(clazz, getGrailsDomainBinder()) - } - - protected InFlightMetadataCollectorImpl getCollector() { - def bootstrapServiceRegistry = getServiceRegistry() - .getParentServiceRegistry() - .getParentServiceRegistry() as BootstrapServiceRegistry - def serviceRegistry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry) - .applySetting("hibernate.dialect", H2Dialect.class.getName()) - .applySetting("jakarta.persistence.jdbc.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") - .applySetting("jakarta.persistence.jdbc.driver", "org.h2.Driver") - .build() - def options = new MetadataBuilderImpl( - new MetadataSources(serviceRegistry) - ).getMetadataBuildingOptions() - new InFlightMetadataCollectorImpl( - new BootstrapContextImpl( serviceRegistry, options) - , options); - } - - protected HibernateMappingContext getMappingContext() { - manager.hibernateDatastore.getMappingContext() - } - - protected GrailsDomainBinder getGrailsDomainBinder() { - def registry = getServiceRegistry() - registry - .getParentServiceRegistry() - .getService(ClassLoaderService.class) - .loadJavaServices(AdditionalMappingContributor.class) - .find { it instanceof GrailsDomainBinder } - } - - protected ServiceRegistryImplementor getServiceRegistry() { - getSessionFactory() - .getServiceRegistry() - } - - protected SessionFactoryImpl getSessionFactory() { - manager.hibernateDatastore.sessionFactory as SessionFactoryImpl - } - - protected HibernateDatastore getDatastore() { - manager.hibernateDatastore - } - - - protected AbstractHibernateSession getSession() { - datastore.connect() as AbstractHibernateSession - } - - protected PersistentEntity getPersistentEntity(Class clazz) { - getMappingContext().getPersistentEntity(clazz.typeName) - } - - protected HibernateQuery getQuery(Class clazz) { - return new HibernateQuery(session, getPersistentEntity(clazz)) - } -} diff --git a/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/org/apache/grails/data/hibernate6/core/GrailsDataHibernate6TckManager.groovy b/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/org/apache/grails/data/hibernate6/core/GrailsDataHibernate6TckManager.groovy deleted file mode 100644 index 5d278f88bd..0000000000 --- a/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/org/apache/grails/data/hibernate6/core/GrailsDataHibernate6TckManager.groovy +++ /dev/null @@ -1,132 +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.apache.grails.data.hibernate6.core - -import grails.core.DefaultGrailsApplication -import grails.core.GrailsApplication -import groovy.sql.Sql -import org.apache.grails.data.testing.tck.base.GrailsDataTckManager -import org.grails.datastore.mapping.core.DatastoreUtils -import org.grails.datastore.mapping.core.Session -import org.grails.orm.hibernate.GrailsHibernateTransactionManager -import org.grails.orm.hibernate.HibernateDatastore -import org.grails.orm.hibernate.cfg.HibernateMappingContextConfiguration -import org.h2.Driver -import org.hibernate.SessionFactory -import org.springframework.beans.factory.DisposableBean -import org.springframework.context.ApplicationContext -import org.springframework.orm.hibernate5.SessionFactoryUtils -import org.springframework.orm.hibernate5.SessionHolder -import org.springframework.transaction.TransactionStatus -import org.springframework.transaction.support.DefaultTransactionDefinition -import org.springframework.transaction.support.TransactionSynchronizationManager -import spock.lang.Specification - -class GrailsDataHibernate6TckManager extends GrailsDataTckManager { - GrailsApplication grailsApplication - HibernateDatastore hibernateDatastore - org.hibernate.Session hibernateSession - GrailsHibernateTransactionManager transactionManager - SessionFactory sessionFactory - TransactionStatus transactionStatus - HibernateMappingContextConfiguration hibernateConfig - ApplicationContext applicationContext - ConfigObject grailsConfig = new ConfigObject() - boolean isTransactional = true - - @Override - void setup(Class<? extends Specification> spec) { - cleanRegistry() - super.setup(spec) - } - - @Override - Session createSession() { - System.setProperty('hibernate6.gorm.suite', "true") - grailsApplication = new DefaultGrailsApplication(domainClasses as Class[], new GroovyClassLoader(GrailsDataHibernate6TckManager.getClassLoader())) - grailsConfig.dataSource.dbCreate = "create-drop" - grailsConfig.hibernate.proxy_factory_class = "yakworks.hibernate.proxy.ByteBuddyGroovyProxyFactory" - if (grailsConfig) { - grailsApplication.config.putAll(grailsConfig) - } - hibernateDatastore = new HibernateDatastore(DatastoreUtils.createPropertyResolver(grailsConfig), domainClasses as Class[]) - transactionManager = hibernateDatastore.getTransactionManager() - sessionFactory = hibernateDatastore.sessionFactory - if (transactionStatus == null && isTransactional) { - transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()) - } else if (isTransactional) { - throw new RuntimeException("new transaction started during active transaction") - } - if (!isTransactional) { - hibernateSession = sessionFactory.openSession() - TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(hibernateSession)) - } else { - hibernateSession = sessionFactory.currentSession - } - - return hibernateDatastore.connect() - } - - @Override - void destroy() { - super.destroy() - - if (transactionStatus != null) { - def tx = transactionStatus - transactionStatus = null - transactionManager.rollback(tx) - } - if (hibernateSession != null) { - SessionFactoryUtils.closeSession((org.hibernate.Session) hibernateSession) - } - - if (hibernateConfig != null) { - hibernateConfig = null - } - if (hibernateDatastore != null) { - hibernateDatastore.destroy() - } - grailsApplication = null - hibernateDatastore = null - hibernateSession = null - transactionManager = null - sessionFactory = null - if (applicationContext instanceof DisposableBean) { - applicationContext.destroy() - } - applicationContext = null - shutdownInMemDb() - } - - private void shutdownInMemDb() { - Sql sql = null - try { - sql = Sql.newInstance('jdbc:h2:mem:grailsDb', 'sa', '', Driver.name) - sql.executeUpdate('SHUTDOWN') - } catch (e) { - // already closed, ignore - } finally { - try { - sql?.close() - } catch (ignored) { - } - } - } -} diff --git a/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy b/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy deleted file mode 100644 index 3042533827..0000000000 --- a/grails-data-hibernate7/core/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy +++ /dev/null @@ -1,73 +0,0 @@ -package org.grails.orm.hibernate.proxy - -import grails.gorm.annotation.Entity -import grails.gorm.specs.HibernateGormDatastoreSpec -import org.hibernate.Hibernate -import spock.lang.Shared - -class HibernateProxyHandlerSpec extends HibernateGormDatastoreSpec { - - @Shared HibernateProxyHandler proxyHandler = new HibernateProxyHandler() - - void "test isInitialized for a non-proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - - expect: - proxyHandler.isInitialized(location) == true - } - - void "test isInitialized for a proxied object before initialization"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - // Get a proxy without initializing it - Location proxyLocation = Location.load(location.id) - - expect: - proxyHandler.isInitialized(proxyLocation) == false - !Hibernate.isInitialized(proxyLocation) - } - - void "test isInitialized for a proxied object after initialization"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - Location proxyLocation = Location.load(location.id) - proxyLocation.name // Accessing a property to initialize the proxy - - expect: - proxyHandler.isInitialized(proxyLocation) == true - Hibernate.isInitialized(proxyLocation) - } - - void "test unwrap for a non-proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - - expect: - proxyHandler.unwrap(location) == location - } - - void "test unwrap for a proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - Location proxyLocation = Location.load(location.id) - def unwrapped = proxyHandler.unwrap(proxyLocation) - - expect: - unwrapped != proxyLocation - unwrapped.name == location.name // After unwrap, it should be initialized and contain original data - } -} - -@Entity -class Location { - Long id - Long version - String name -} diff --git a/grails-data-hibernate7/core/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy b/grails-data-hibernate7/core/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy deleted file mode 100644 index 3042533827..0000000000 --- a/grails-data-hibernate7/core/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandlerSpec.groovy +++ /dev/null @@ -1,73 +0,0 @@ -package org.grails.orm.hibernate.proxy - -import grails.gorm.annotation.Entity -import grails.gorm.specs.HibernateGormDatastoreSpec -import org.hibernate.Hibernate -import spock.lang.Shared - -class HibernateProxyHandlerSpec extends HibernateGormDatastoreSpec { - - @Shared HibernateProxyHandler proxyHandler = new HibernateProxyHandler() - - void "test isInitialized for a non-proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - - expect: - proxyHandler.isInitialized(location) == true - } - - void "test isInitialized for a proxied object before initialization"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - // Get a proxy without initializing it - Location proxyLocation = Location.load(location.id) - - expect: - proxyHandler.isInitialized(proxyLocation) == false - !Hibernate.isInitialized(proxyLocation) - } - - void "test isInitialized for a proxied object after initialization"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - Location proxyLocation = Location.load(location.id) - proxyLocation.name // Accessing a property to initialize the proxy - - expect: - proxyHandler.isInitialized(proxyLocation) == true - Hibernate.isInitialized(proxyLocation) - } - - void "test unwrap for a non-proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - - expect: - proxyHandler.unwrap(location) == location - } - - void "test unwrap for a proxied object"() { - given: - Location location = new Location(name: "Test Location").save(flush: true) - manager.session.clear() - - Location proxyLocation = Location.load(location.id) - def unwrapped = proxyHandler.unwrap(proxyLocation) - - expect: - unwrapped != proxyLocation - unwrapped.name == location.name // After unwrap, it should be initialized and contain original data - } -} - -@Entity -class Location { - Long id - Long version - String name -}
