apache / apache/grails-core

Every 'hasOne' Relationship is eagerly fetched by default and cannot be configured as lazy: true

Open
#14,469 2 comments 4 reactions 0 assignees View on GitHub
relates-to: grails-data-hibernate5
Dominant language
Groovy
Stars
2.9k
Forks
975
Avg merge
1d 22h
Merged PRs (30d)
92

Description

Consider two domain entities:

```
@Entity
class AParent {
// ...
static hasOne = [child:AChild]
// ...
}
```

```
@Entity
class AChild {
// ...
static belongsTo = [parent:AParent]
// ...
}
```

If I have a list of 10 ids for Entity 'AParent' and make the following call:
`AParent.findAllByIdInList( aIds )`

This single call (without _ever_ attempting to programmatically access 'child') immediately triggers 10 additional hibernate sql selects to get each individual child (where each AParent hasOne AChild).
And so one 'findAllByIdInList' call triggers 11 db calls, with no explicit requests of the child entity and explicit lazy configuration:

Additionally, there is no way to disable this eager fetching. The following static mappings fail to have the desired impact:
- child fetch: 'select'
- child fetch: 'lazy'
- child lazy: 'true'

Example output from the 1 call `AParent.findAllByIdInList( aIds ) // with 10 ids`

```Hibernate: select this_.id as id1_1_0_, this_.version as version2_1_0_, this_.parent_description as parent_d3_1_0_ from a_parent this_ where this_.id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
Hibernate: select achild0_.id as id1_0_0_, achild0_.version as version2_0_0_, achild0_.description as descript3_0_0_, achild0_.parent_id as parent_i4_0_0_ from a_child achild0_ where achild0_.parent_id=?
```

The main issue is that this relationship *cannot* be configured as lazy (at least not any way that I am aware of, and traditional static mappings do not work).

Less importantly, it could be argued that the default for the hasOne _should_ be lazy (like the hasMany), but this is not the case.

This can be easily reproduced with the following Micronaut 1.1.1 + GORM 7.0.0 Repository:
https://github.com/alanbino/gorm-hasone

Contributor guide

Open the contributing guide

Research direction

Start with the AParent and AChild mappings and the AParent.findAllByIdInList(aIds) call described in the issue, then reproduce the behavior using the linked Micronaut 1.1.1 and GORM 7.0.0 repository. Compare the generated SQL with the configured fetch and lazy options; done means hasOne can be configured without issuing child selects when the relationship is not accessed.

Written by the indexing model from the issue text.

Assessment

Tech stack
groovy
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.