Michal Joštiak
Michal Joštiak

Reputation: 171

Mapping hasOne with belongsTo in Grails

Legacy database example (desc below): enter image description here

I have the Parent table that has composite primary key, id1 autogenerated as SERIAL(1), id2 with only default value. Child table has exactly same primary key, that is also foreign key from Parent table.

Child table is something like "plus" table, or extanded Parent table. We need additional columns in PArent table but we cant change the Parent table. So they created for us Child table with additional information, add foreign key from Parent table to Child table and also make this composite key as primary :)

I try to map it in Grails this way:

Parent.groovy

package test
class Parent implements Serializable{
    Integer id1
    Integer id2
    String  parentDesc

    static hasOne = [child: Child]

    static mappedBy = [child: 'parent']

    static constraints = {
        child nullable:true
    }

    static mapping = {
        table 'parent'
        id         composite: ['id1', 'id2']
        id1        column: 'id1'
        id2        column: 'id2'
        parentDesc column: 'parent_desc'
    }
}

Child.groovy

package test

import java.io.Serializable

class Child  implements Serializable{
    Integer id1
    Integer id2
    Parent parent
    String  childDesc

    static belongsTo = [parent: Parent]

    static mapping = {
        table 'child'


      id        composite: ['id1', 'id2']
      id1       column: 'id1'
      id2       column: 'id2'
      childDesc column: 'child_desc'
      columns {
         parent(insertable: false, updateable: false) {
            column name: 'id1'
            column name: 'id2'
         }
    }
}

} The Parent has one or zero Child with composite key id1, id2. The Child belongs to Parent (save and delete with parent), has foreign key Parent's primary key, and also this composite foreign key is also his primary key.

Hibernate cannot return values when there are some rows (empty table is no problem :) - I think that its same bug reported here (but this time already resolved).

Technical info: Database: MaxDB (legacy) also tested on H2 embedded

Full test project: dropbox

Stacktrace:

[2m2017-06-05 13:59:26.063[0;39m [31mERROR[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [36mo.g.web.errors.GrailsExceptionResolver  [0;39m [2m:[0;39m NullPointerException occurred when processing request: [GET] /
Stacktrace follows:

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:963)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    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:748)
Caused by: java.lang.NullPointerException: null
    at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2172)
    at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:692)
    at org.hibernate.type.EntityType.resolve(EntityType.java:434)
    at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:151)
    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.doList(Loader.java:2622)
    at org.hibernate.loader.Loader.doList(Loader.java:2605)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2434)
    at org.hibernate.loader.Loader.list(Loader.java:2429)
    at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:109)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1787)
    at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:363)
    at org.grails.orm.hibernate.query.AbstractHibernateQuery.listForCriteria(AbstractHibernateQuery.java:700)
    at org.grails.orm.hibernate.HibernateGormStaticApi$_list_closure1.doCall(HibernateGormStaticApi.groovy:87)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:286)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:230)
    at org.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:116)
    at org.grails.orm.hibernate.HibernateGormStaticApi.list(HibernateGormStaticApi.groovy:73)
    at org.grails.orm.hibernate.HibernateGormStaticApi.list(HibernateGormStaticApi.groovy:72)
    at org.grails.datastore.gorm.GormEntity$Trait$Helper.list(GormEntity.groovy:654)
    at test.ParentController.$tt__index(ParentController.groovy:10)
    at grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
    at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
    ... 14 common frames omitted

Upvotes: 1

Views: 759

Answers (1)

Renatols
Renatols

Reputation: 388

I know this question is somewhat old, but I had what I think is the same problem as yours.

Anyway, after some debugging and research, I got mine working. All I had to do was switch the fetch behavior to 'join' so it will fetch the parent and the child in the same sql query. Note that this could mean a performance bottleneck depeding on your use case.

static mapping = {
    child fetch: 'join'
}

Edit: refresh() still doesn't work though

Upvotes: 1

Related Questions