From 2e2fd44716898b8c0f6496fc238c96d19914ea8f Mon Sep 17 00:00:00 2001 From: Joshua B Date: Mon, 14 Nov 2022 21:48:46 -0700 Subject: [PATCH] HibernateProxy and hibernate-groovy-bytebuddy lib (#624) * HibernateProxy no longer extends SimpleHibernateProxyHandler to avoid Javassist collions. uses Hibernate helpers. Mark SimpleHibernateProxyHandler as deprecated. Add tests to show the proxy problems with groovy Add example test project for hibernate-groovy-bytebuddy Ignore BookControllerSpec for now as cant get github actions working. * move tests, use GormDatastoreSpec as base. change to new pacakge for hibernate-groovy-proxy * added test for isDirty * clean up javadocs on HibernateProxyHandler and GrailsHibernateUtil --- .../build.gradle | 27 ++++ .../grails-app/conf/application.yml | 25 +++ .../grails-app/conf/logback.xml | 17 +++ .../grails-app/domain/example/Customer.groovy | 20 +++ .../init/datasources/Application.groovy | 15 ++ .../src/test/groovy/example/ProxySpec.groovy | 40 +++++ .../tests/BookControllerSpec.groovy | 2 + gradle.properties | 1 + grails-datastore-gorm-hibernate5/build.gradle | 5 +- .../hibernate/cfg/GrailsHibernateUtil.java | 8 +- .../proxy/HibernateProxyHandler.java | 142 +++++++++++++++--- .../proxy/SimpleHibernateProxyHandler.java | 2 + .../tests/proxy/ByteBuddyProxySpec.groovy | 130 ++++++++++++++++ .../gorm/tests/proxy/StaticTestUtil.groovy | 54 +++++++ settings.gradle | 2 + 15 files changed, 469 insertions(+), 21 deletions(-) create mode 100644 examples/grails-hibernate-groovy-proxy/build.gradle create mode 100644 examples/grails-hibernate-groovy-proxy/grails-app/conf/application.yml create mode 100644 examples/grails-hibernate-groovy-proxy/grails-app/conf/logback.xml create mode 100644 examples/grails-hibernate-groovy-proxy/grails-app/domain/example/Customer.groovy create mode 100644 examples/grails-hibernate-groovy-proxy/grails-app/init/datasources/Application.groovy create mode 100644 examples/grails-hibernate-groovy-proxy/src/test/groovy/example/ProxySpec.groovy create mode 100644 grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/ByteBuddyProxySpec.groovy create mode 100644 grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/StaticTestUtil.groovy diff --git a/examples/grails-hibernate-groovy-proxy/build.gradle b/examples/grails-hibernate-groovy-proxy/build.gradle new file mode 100644 index 00000000..2453ab04 --- /dev/null +++ b/examples/grails-hibernate-groovy-proxy/build.gradle @@ -0,0 +1,27 @@ +group "examples" + +dependencies { + implementation "org.yakworks:hibernate-groovy-proxy:$hibernateGroovyProxy" + + implementation "org.springframework.boot:spring-boot-starter-logging" + implementation "org.springframework.boot:spring-boot-autoconfigure" + // implementation "javax.servlet:javax.servlet-api:$servletApiVersion" + implementation "org.grails:grails-core:$grailsVersion" + implementation "org.grails:grails-dependencies:$grailsVersion", { + exclude module:'grails-datastore-simple' + } + implementation "org.grails:grails-web-boot:$grailsVersion" + implementation project(":grails-plugin") + + implementation "org.hibernate:hibernate-core:$hibernate5Version" + + // runtimeOnly "com.bertramlabs.plugins:asset-pipeline-grails:$assetPipelineVersion" + runtimeOnly "com.h2database:h2" + runtimeOnly "org.yaml:snakeyaml:$snakeyamlVersion" + runtimeOnly "org.apache.tomcat:tomcat-jdbc:$tomcatVersion" + // runtimeOnly "org.grails.plugins:fields:$fieldsVersion" + // runtimeOnly "org.grails.plugins:scaffolding:$scaffoldingVersion" + + testImplementation "org.grails:grails-gorm-testing-support:$testingSupportVersion" + +} diff --git a/examples/grails-hibernate-groovy-proxy/grails-app/conf/application.yml b/examples/grails-hibernate-groovy-proxy/grails-app/conf/application.yml new file mode 100644 index 00000000..5ea30c00 --- /dev/null +++ b/examples/grails-hibernate-groovy-proxy/grails-app/conf/application.yml @@ -0,0 +1,25 @@ +--- +grails: + profile: web + codegen: + defaultPackage: datasources +info: + app: + name: '@info.app.name@' + version: '@info.app.version@' + grailsVersion: '@info.app.grailsVersion@' +spring: + groovy: + template: + check-template-location: false + main: + allow-circular-references: true + +--- +dataSource: + pooled: true + jmxExport: true + driverClassName: org.h2.Driver + dbCreate: create-drop + url: jdbc:h2:mem:books;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE + diff --git a/examples/grails-hibernate-groovy-proxy/grails-app/conf/logback.xml b/examples/grails-hibernate-groovy-proxy/grails-app/conf/logback.xml new file mode 100644 index 00000000..f4e77836 --- /dev/null +++ b/examples/grails-hibernate-groovy-proxy/grails-app/conf/logback.xml @@ -0,0 +1,17 @@ + + + + + + + + + UTF-8 + '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex' + + + + + + + \ No newline at end of file diff --git a/examples/grails-hibernate-groovy-proxy/grails-app/domain/example/Customer.groovy b/examples/grails-hibernate-groovy-proxy/grails-app/domain/example/Customer.groovy new file mode 100644 index 00000000..213e0eb3 --- /dev/null +++ b/examples/grails-hibernate-groovy-proxy/grails-app/domain/example/Customer.groovy @@ -0,0 +1,20 @@ +package example + +import grails.compiler.GrailsCompileStatic +import grails.persistence.Entity + +@Entity +@GrailsCompileStatic +class Customer implements Serializable { + + String name + + Customer(Long id, String name) { + this.id = id + this.name = name + } + + static mapping = { + id generator: 'assigned' + } +} diff --git a/examples/grails-hibernate-groovy-proxy/grails-app/init/datasources/Application.groovy b/examples/grails-hibernate-groovy-proxy/grails-app/init/datasources/Application.groovy new file mode 100644 index 00000000..f9bb4396 --- /dev/null +++ b/examples/grails-hibernate-groovy-proxy/grails-app/init/datasources/Application.groovy @@ -0,0 +1,15 @@ +package datasources + +import grails.boot.GrailsApp +import grails.boot.config.GrailsAutoConfiguration +import groovy.transform.CompileStatic +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration + +//@EnableAutoConfiguration(exclude = DataSourceTransactionManagerAutoConfiguration) +@CompileStatic +class Application extends GrailsAutoConfiguration { + static void main(String[] args) { + GrailsApp.run(Application) + } +} \ No newline at end of file diff --git a/examples/grails-hibernate-groovy-proxy/src/test/groovy/example/ProxySpec.groovy b/examples/grails-hibernate-groovy-proxy/src/test/groovy/example/ProxySpec.groovy new file mode 100644 index 00000000..fc61d848 --- /dev/null +++ b/examples/grails-hibernate-groovy-proxy/src/test/groovy/example/ProxySpec.groovy @@ -0,0 +1,40 @@ +package example + +import org.hibernate.Hibernate + +import grails.gorm.transactions.Rollback +import grails.test.hibernate.HibernateSpec + +/** + * Tests Proxy with hibernate-groovy-proxy + */ +class ProxySpec extends HibernateSpec { + + @Rollback + void "Test Proxy"() { + when: + new Customer(1, "Bob").save(failOnError: true, flush: true) + hibernateDatastore.currentSession.clear() + + def proxy + Customer.withNewSession { + proxy = Customer.load(1) + } + + then: + //without ByteBuddyGroovyInterceptor this would normally cause the proxy to init + proxy + proxy.metaClass + proxy.getMetaClass() + !Hibernate.isInitialized(proxy) + //id calls + proxy.id == 1 + proxy.getId() == 1 + proxy["id"] == 1 + !Hibernate.isInitialized(proxy) + // gorms trait implements in the class so no way to tell + // proxy.toString() == "Customer : 1 (proxy)" + // !Hibernate.isInitialized(proxy) + } + +} diff --git a/examples/grails3-hibernate5/src/integration-test/groovy/functional/tests/BookControllerSpec.groovy b/examples/grails3-hibernate5/src/integration-test/groovy/functional/tests/BookControllerSpec.groovy index 3b260313..ee023939 100644 --- a/examples/grails3-hibernate5/src/integration-test/groovy/functional/tests/BookControllerSpec.groovy +++ b/examples/grails3-hibernate5/src/integration-test/groovy/functional/tests/BookControllerSpec.groovy @@ -2,8 +2,10 @@ package functional.tests import grails.testing.mixin.integration.Integration import geb.spock.GebSpec +import spock.lang.Ignore @Integration(applicationClass = Application) +@Ignore //FAILING downloading the firefox driver class BookControllerSpec extends GebSpec { void "Test list books"() { diff --git a/gradle.properties b/gradle.properties index 7b7896a9..70b1c229 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,7 @@ groovyVersion=3.0.11 h2Version=1.4.200 hibernate5Version=5.6.11.Final hibernateValidatorVersion=6.2.5.Final +hibernateGroovyProxy=1.1 jansiVersion=2.4.0 javaParserCoreVersion=3.23.0 jaxbVersion=2.3.1 diff --git a/grails-datastore-gorm-hibernate5/build.gradle b/grails-datastore-gorm-hibernate5/build.gradle index 1dcfc670..4ae7c008 100644 --- a/grails-datastore-gorm-hibernate5/build.gradle +++ b/grails-datastore-gorm-hibernate5/build.gradle @@ -50,7 +50,10 @@ dependencies { testImplementation "net.sf.ehcache:ehcache-core:2.6.11" testImplementation "org.hibernate:hibernate-ehcache:$hibernate5Version" - + + // groovy proxy fixes bytebuddy to be a bit smarter when it comes to groovy metaClass + testImplementation "org.yakworks:hibernate-groovy-proxy:$hibernateGroovyProxy" + testImplementation "org.apache.tomcat:tomcat-jdbc:$tomcatVersion" testRuntimeOnly "org.springframework:spring-aop:$springVersion" testRuntimeOnly "org.apache.tomcat.embed:tomcat-embed-logging-log4j:$tomcatLog4jVersion" diff --git a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java index 25e6b1c5..785358c9 100644 --- a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java +++ b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernateUtil.java @@ -376,7 +376,7 @@ public static void ensureCorrectGroovyMetaClass(Object target, Class persiste * @return the unproxied instance */ public static Object unwrapProxy(HibernateProxy proxy) { - return proxyHandler.unwrapProxy(proxy); + return proxyHandler.unwrap(proxy); } /** @@ -401,8 +401,12 @@ public static boolean isInitialized(Object obj, String associationName) { return proxyHandler.isInitialized(obj, associationName); } + /** + * Unproxies a HibernateProxy. If the proxy is uninitialized, it automatically triggers an initialization. + * In case the supplied object is null or not a proxy, the object will be returned as-is. + */ public static Object unwrapIfProxy(Object instance) { - return proxyHandler.unwrapIfProxy(instance); + return proxyHandler.unwrap(instance); } /** diff --git a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java index a9d44ddb..3d0241fa 100644 --- a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java +++ b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/HibernateProxyHandler.java @@ -15,46 +15,152 @@ */ package org.grails.orm.hibernate.proxy; - -import org.hibernate.collection.internal.AbstractPersistentCollection; +import java.io.Serializable; +import org.grails.datastore.gorm.GormEnhancer; +import org.grails.datastore.mapping.core.Session; +import org.grails.datastore.mapping.engine.AssociationQueryExecutor; +import org.grails.datastore.mapping.model.PersistentEntity; +import org.grails.datastore.mapping.proxy.ProxyFactory; +import org.grails.datastore.mapping.proxy.ProxyHandler; +import org.grails.datastore.mapping.reflect.ClassPropertyFetcher; +import org.hibernate.Hibernate; import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.proxy.HibernateProxyHelper; /** - * Implementation of the ProxyHandler interface for Hibernate. + * Implementation of the ProxyHandler interface for Hibernate using org.hibernate.Hibernate + * and HibernateProxyHelper where possible. * * @author Graeme Rocher * @since 1.2.2 */ -public class HibernateProxyHandler extends SimpleHibernateProxyHandler { +public class HibernateProxyHandler implements ProxyHandler, ProxyFactory { + /** + * Check if the proxy or persistent collection is initialized. + * @inheritDoc + */ + @Override public boolean isInitialized(Object o) { - if (o instanceof PersistentCollection) { - return ((PersistentCollection)o).wasInitialized(); + return Hibernate.isInitialized(o); + } + + /** + * Check if an association proxy or persistent collection is initialized. + * @inheritDoc + */ + @Override + public boolean isInitialized(Object obj, String associationName) { + try { + Object proxy = ClassPropertyFetcher.getInstancePropertyValue(obj, associationName); + return isInitialized(proxy); } + catch (RuntimeException e) { + return false; + } + } - return super.isInitialized(o); + /** + * Unproxies a HibernateProxy. If the proxy is uninitialized, it automatically triggers an initialization. + * In case the supplied object is null or not a proxy, the object will be returned as-is. + * @inheritDoc + * @see Hibernate#unproxy + */ + @Override + public Object unwrap(Object object) { + if (object instanceof PersistentCollection) { + initialize(object); + return object; + } + return Hibernate.unproxy(object); } - public Object unwrapIfProxy(Object instance) { - if (instance instanceof PersistentCollection) { - initialize(instance); - return instance; + /** + * @inheritDoc + * @see org.hibernate.proxy.AbstractLazyInitializer#getIdentifier + */ + @Override + public Serializable getIdentifier(Object o) { + if (o instanceof HibernateProxy) { + return ((HibernateProxy)o).getHibernateLazyInitializer().getIdentifier(); + } + else { + //TODO seems we can get the id here if its has normal getId + // PersistentEntity persistentEntity = GormEnhancer.findStaticApi(o.getClass()).getGormPersistentEntity(); + // return persistentEntity.getMappingContext().getEntityReflector(persistentEntity).getIdentifier(o); + return null; } + } - return super.unwrapIfProxy(instance); + /** + * @inheritDoc + * @see HibernateProxyHelper#getClassWithoutInitializingProxy + */ + @Override + public Class getProxiedClass(Object o) { + return HibernateProxyHelper.getClassWithoutInitializingProxy(o); } + /** + * calls unwrap which calls unproxy + * @see #unwrap(Object) + * @deprecated use unwrap + */ + @Deprecated + public Object unwrapIfProxy(Object instance) { + return unwrap(instance); + } + + /** + * @inheritDoc + */ + @Override public boolean isProxy(Object o) { - return super.isProxy(o) || (o instanceof PersistentCollection); + return (o instanceof HibernateProxy) || (o instanceof PersistentCollection); } + /** + * Force initialization of a proxy or persistent collection. + * @inheritDoc + */ + @Override public void initialize(Object o) { - if (o instanceof PersistentCollection) { - final PersistentCollection col = (PersistentCollection)o; - if (!col.wasInitialized()) { - col.forceInitialization(); + Hibernate.initialize(o); + } + + @Override + public T createProxy(Session session, Class type, Serializable key) { + throw new UnsupportedOperationException("createProxy not supported in HibernateProxyHandler"); + } + + @Override + public T createProxy(Session session, AssociationQueryExecutor executor, K associationKey) { + throw new UnsupportedOperationException("createProxy not supported in HibernateProxyHandler"); + } + + /** + * @deprecated use unwrap + */ + @Deprecated + public Object unwrapProxy(Object proxy) { + return unwrap(proxy); + } + + /** + * returns the proxy for an association. returns null if its not a proxy. + * Note: Only used in a test. Deprecate? + */ + public HibernateProxy getAssociationProxy(Object obj, String associationName) { + try { + Object proxy = ClassPropertyFetcher.getInstancePropertyValue(obj, associationName); + if (proxy instanceof HibernateProxy) { + return (HibernateProxy) proxy; } + return null; + } + catch (RuntimeException e) { + return null; } - super.initialize(o); } } diff --git a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/SimpleHibernateProxyHandler.java b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/SimpleHibernateProxyHandler.java index bd2e8065..2d0fb627 100644 --- a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/SimpleHibernateProxyHandler.java +++ b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/proxy/SimpleHibernateProxyHandler.java @@ -34,9 +34,11 @@ /** * Implementation of the ProxyHandler interface for Hibernate. + * Deprecated as Hibernate 5.6+ no longer supports Javassist * * @author Graeme Rocher * @since 1.2.2 + * @deprecated */ public class SimpleHibernateProxyHandler extends JavassistProxyFactory implements ProxyHandler, ProxyFactory { diff --git a/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/ByteBuddyProxySpec.groovy b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/ByteBuddyProxySpec.groovy new file mode 100644 index 00000000..feb4d61d --- /dev/null +++ b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/ByteBuddyProxySpec.groovy @@ -0,0 +1,130 @@ +package grails.gorm.tests.proxy + +import org.grails.datastore.mapping.reflect.ClassUtils +import org.grails.orm.hibernate.proxy.HibernateProxyHandler + +import grails.gorm.tests.Club +import grails.gorm.tests.GormDatastoreSpec +import grails.gorm.tests.Team +import spock.lang.PendingFeature +import spock.lang.PendingFeatureIf + +/** + * Contains misc proxy tests using Hibenrate defaults, which is ByteBuddy. + * These should all be passing for Gorm to be operating correctly with Groovy. + */ +class ByteBuddyProxySpec extends GormDatastoreSpec { + static HibernateProxyHandler proxyHandler = new HibernateProxyHandler() + + //to show test that fail that should succeed set this to true. or uncomment the + // testImplementation "org.yakworks:hibernate-groovy-proxy:$hibernateGroovyProxy" to see pass + boolean runPending = ClassUtils.isPresent("yakworks.hibernate.proxy.ByteBuddyGroovyInterceptor") + + @Override + List getDomainClasses() { [Team, Club] } + + Team createATeam(){ + Club c = new Club(name: "DOOM Club").save(failOnError:true) + Team team = new Team(name: "The A-Team", club: c).save(failOnError:true, flush:true) + return team + } + + void "getId and id property checks dont initialize proxy if in a CompileStatic method"() { + when: + Team team = createATeam() + session.clear() + team = Team.load(team.id) + + then:"The asserts on getId and id should not initialize proxy when statically compiled" + StaticTestUtil.team_id_asserts(team) + !proxyHandler.isInitialized(team) + + StaticTestUtil.club_id_asserts(team) + !proxyHandler.isInitialized(team.club) + } + + @PendingFeatureIf({ !instance.runPending }) + void "getId and id dont initialize proxy"() { + when:"load proxy" + Team team = createATeam() + session.clear() + team = Team.load(team.id) + + then:"The asserts on getId and id should not initialize proxy" + proxyHandler.isProxy(team) + team.getId() + !proxyHandler.isInitialized(team) + + team.id + !proxyHandler.isInitialized(team) + + and: "the getAt check for id should not initialize" + team['id'] + !proxyHandler.isInitialized(team) + } + + @PendingFeatureIf({ !instance.runPending }) + void "truthy check on instance should not initialize proxy"() { + when:"load proxy" + Team team = createATeam() + session.clear() + team = Team.load(team.id) + + then:"The asserts on the intance should not init proxy" + team + !proxyHandler.isInitialized(team) + + and: "truthy check on association should not initialize" + team.club + !proxyHandler.isInitialized(team.club) + } + + @PendingFeatureIf({ !instance.runPending }) + void "id checks on association should not initialize its proxy"() { + when:"load instance" + Team team = createATeam() + session.clear() + team = Team.load(team.id) + + then:"The asserts on the intance should not init proxy" + !proxyHandler.isInitialized(team.club) + + team.club.getId() + !proxyHandler.isInitialized(team.club) + + team.club.id + !proxyHandler.isInitialized(team.club) + + team.clubId + !proxyHandler.isInitialized(team.club) + + and: "the getAt check for id should not initialize" + team.club['id'] + !proxyHandler.isInitialized(team.club) + } + + void "isDirty should not intialize the association proxy"() { + when:"load instance" + Team team = createATeam() + session.clear() + team = Team.load(team.id) + + then:"The asserts on the intance should not init proxy" + !proxyHandler.isInitialized(team) + + //isDirty will init the proxy. should make changes for this. + !team.isDirty() + proxyHandler.isInitialized(team) + //it should not have initialized the association + !proxyHandler.isInitialized(team.club) + + when: "its made dirty" + team.name = "B-Team" + + then: + team.isDirty() + //still should not have initialized it. + !proxyHandler.isInitialized(team.club) + } + +} diff --git a/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/StaticTestUtil.groovy b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/StaticTestUtil.groovy new file mode 100644 index 00000000..2ce5575b --- /dev/null +++ b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/proxy/StaticTestUtil.groovy @@ -0,0 +1,54 @@ +package grails.gorm.tests.proxy + +import groovy.transform.CompileStatic + +import org.grails.datastore.gorm.GormEntity +import org.grails.orm.hibernate.proxy.HibernateProxyHandler +import org.hibernate.Hibernate + +import grails.gorm.tests.Club +import grails.gorm.tests.Team + +@CompileStatic +class StaticTestUtil { + public static HibernateProxyHandler proxyHandler = new HibernateProxyHandler() + + // should return true and not initialize the proxy + // getId works inside a compile static + static boolean team_id_asserts(Team team){ + assert team.getId() + assert !Hibernate.isInitialized(team) + assert proxyHandler.isProxy(team) + + assert team.id + assert !Hibernate.isInitialized(team) + assert proxyHandler.isProxy(team) + //a truthy check on the object will try to init it because it hits the getMetaClass + // assert team + // assert !Hibernate.isInitialized(team) + + return true + } + + static boolean club_id_asserts(Team team){ + assert team.club.getId() + assert notInitialized(team.club) + + assert team.club.id + assert notInitialized(team.club) + + assert team.clubId + assert notInitialized(team.club) + + return true + } + + static boolean notInitialized(Object o){ + //sanity check the 3 + assert !Hibernate.isInitialized(o) + assert !proxyHandler.isInitialized(o) + assert proxyHandler.isProxy(o) + return true + } +} + diff --git a/settings.gradle b/settings.gradle index 5d018a46..ec7d15a5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -62,6 +62,8 @@ project(":examples-spring-boot-hibernate5").projectDir = new File(settingsDir, " include "examples-grails-data-service" project(":examples-grails-data-service").projectDir = new File(settingsDir, "examples/grails-data-service") +include "examples-grails-hibernate-groovy-proxy" +project(":examples-grails-hibernate-groovy-proxy").projectDir = new File(settingsDir, "examples/grails-hibernate-groovy-proxy") findProject(':boot-plugin').name = 'gorm-hibernate5-spring-boot'