grails / grails-data-mapping

GORM - Groovy Object Mapping
http://gorm.grails.org/
218 stars 198 forks source link

Refresh doesn't work for entities which contains hasMany mapping. #1210

Open dmkolpakov opened 5 years ago

dmkolpakov commented 5 years ago

After upgrade grails to version to 3.3.9 and GORM version to 6.1.10.RELEASE I've got error during refresh any entity which contains mapping like "hasMany".

Similar problem which I've found here is https://github.com/grails/grails-data-mapping/issues/1096, but no solution was provided.

Link to github(test application): https://github.com/dmkolpakov/grails-data-mapping-issue-1210

Steps for reproduce: Just run provided application from github repository above in HomeController issue will reproduced.

Please pay attention that I've specified access type for properties for entities in application.groovy.

import javax.persistence. grails.gorm.default.mapping = { ''(accessType: AccessType.PROPERTY) }

According my investigation, problem happens because in new version trait DirtyCheckable was added for all domain's entities by default, if we use AccessType.PROPERTY, it invoke setter for certain property and mark entity as dirty, I'm not sure that is right, but more interesting is that hibernate can't proceed with it. I'm going to proceed my investigation, but if you have some thoughts please advise to me.


For example: 

class Vehicle {

    Integer year

    String name
    Model model
    Make make
    static hasMany = [owners: Owner]

    static constraints = {
        year min: 1900
        name maxSize: 255
    }
}

....

 def vehicle = Vehicle.findByName("Pickup")
 vehicle.refresh()

Exception example:

java.lang.reflect.InvocationTargetException: null
    at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:211)
    at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:188)
    at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
    at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
    at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.orm.hibernate5.HibernateSystemException: Exception occurred inside setter of org.grails.guides.Vehicle.owners; nested exception is org.hibernate.PropertyAccessException: Exception occurred inside setter of org.grails.guides.Vehicle.owners
    at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:296)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.convertHibernateAccessException(GrailsHibernateTemplate.java:724)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:303)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.refresh(GrailsHibernateTemplate.java:457)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.refresh(GrailsHibernateTemplate.java:453)
    at org.grails.orm.hibernate.AbstractHibernateGormInstanceApi.refresh(AbstractHibernateGormInstanceApi.groovy:241)
    at org.grails.datastore.gorm.GormEntity$Trait$Helper.refresh(GormEntity.groovy:90)
    at org.grails.guides.HomeController.index(HomeController.groovy:7)
    ... 14 common frames omitted
Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of org.grails.guides.Vehicle.owners
    at org.hibernate.property.access.spi.SetterMethodImpl.set(SetterMethodImpl.java:67)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:664)
    at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:207)
    at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:4692)
    at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:183)
    at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:125)
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1139)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:998)
    at org.hibernate.loader.Loader.doQuery(Loader.java:936)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:342)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:312)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2209)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:60)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:50)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4069)
    at org.hibernate.event.internal.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:171)
    at org.hibernate.event.internal.DefaultRefreshEventListener.onRefresh(DefaultRefreshEventListener.java:48)
    at org.hibernate.internal.SessionImpl.fireRefresh(SessionImpl.java:1193)
    at org.hibernate.internal.SessionImpl.refresh(SessionImpl.java:1166)
    at org.hibernate.internal.SessionImpl.refresh(SessionImpl.java:1161)
    at org.grails.orm.hibernate.GrailsHibernateTemplate$11.doInHibernate(GrailsHibernateTemplate.java:460)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:299)
    ... 19 common frames omitted
Caused by: java.lang.reflect.InvocationTargetException: null
    at org.hibernate.property.access.spi.SetterMethodImpl.set(SetterMethodImpl.java:44)
    ... 40 common frames omitted
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:584)
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:576)
    at org.hibernate.collection.internal.AbstractPersistentCollection.access$200(AbstractPersistentCollection.java:55)
    at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:165)
    at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:146)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:249)
    at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:145)
    at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:143)
    at org.grails.datastore.mapping.dirty.checking.DirtyCheckable$Trait$Helper.markDirty(DirtyCheckable.groovy:81)
    ... 41 common frames omitted

Task List

Environment Information

dmkolpakov commented 5 years ago

Description of issue has been updated.

demon101 commented 5 years ago

@graemerocher in our project we have the same bug with disabled new dirty checking.

hibernate:
    hibernateDirtyChecking: true 

I have checked on https://github.com/dmkolpakov/grails-data-mapping-issue-1210 , the bug still exist with hibernateDirtyChecking: true

By the way, looks like unnecessary dirty checking during refreshing: screenshot from 2019-01-07 13-35-38

demon101 commented 5 years ago

looks like a big regression. Now refresh() working only in simple cases.

As a workaround (thx @dmkolpakov ) empty method trackChanges can be added to the parent class

class Parent implements DirtyCheckable {
    static hasMany = [child: Child]
    void trackChanges() {
        // not track changes
    }
}

But in this case, as I understand parent class become non-DirtyCheckable

demon101 commented 5 years ago

don't use

grails.gorm.default.mapping = {
    '*'(accessType: AccessType.PROPERTY)
}
developerrgm commented 2 years ago

I am also facing same problem. hibernate: hibernateDirtyChecking: true did not help. Any update?