borinquenkid opened a new pull request, #16574:
URL: https://github.com/apache/grails-core/pull/16574
## Problem
An inverse `hasMany` on an entity with a composite identifier that is not
declared in name order loads no elements. With `composite('zeta', 'alpha')` and
`static hasMany = [kids: Kid]` (`Kid belongsTo = [parent: Parent]`),
`parent.kids` is empty while `kid.parent` loads. With `composite('alpha',
'zeta')` it works. In the three level `PrbGrand`/`PrbMiddle`/`PrbLeaf` chain,
`grand.middles` and `middle.leaves` loaded empty in every registration order.
`BidirectionalOneToManyLinker.link` copies the columns of the to-one side,
which the composite foreign key alignment of #16539 has put in identifier order
already, and then called `key.sortProperties()`, which applied the owner
identifier's permutation a second time. The collection key therefore ended up
in a different column order than the to-one key (`prb_middle_grand_parent_zeta,
prb_middle_grand_parent_alpha, prb_middle_name` against `..._alpha, ..._zeta,
..._name`), so the identifier values were bound to the wrong columns. The
analysis is jdaugherty's, from the review of #16539.
## Impact
Hibernate 7 on 8.0.x: any inverse `hasMany`/`oneToMany` whose owner has a
composite identifier declared out of name order silently loads an empty
collection. Saving and the to-one side are unaffected. Single column
identifiers and identifiers declared in name order are unaffected, and the DDL
does not change.
## Fix
The linker marks the copied key as sorted instead of re-sorting it, so the
collection key ends with exactly the column order and types of the to-one side.
The collection's second pass can run before the deferred
`CompositeForeignKeySecondPass` of the to-one side (two of the registration
orders of the chain), so the linker first calls the idempotent
`alignWithReferencedIdentifier` on the to-one side, which does nothing when it
is aligned already. The result is independent of the registration order.
`BidirectionalOneToManyLinker` takes the `CompositeIdentifierToManyToOneBinder`
for this.
## Tests
Red before the fix, green after (all 14 new iterations failed with the
collection empty: the simple parent/kid case in both registration orders, the
chain in all 6 and the hub children in all 6):
- `InverseHasManyCompositeIdentifierOrderSpec`: the previously
`@PendingFeature` case is now a plain feature over the 2 registration
permutations of `IhmParent`/`IhmKid`; it saves, reloads in a new session and
asserts the kids, and that the collection key columns equal the to-one foreign
key columns in order. The name-order case still passes (3 tests).
- `CompositeForeignKeyRegistrationOrderSpec`: two new features. The three
level chain over the 6 permutations of `PrbGrand`/`PrbMiddle`/`PrbLeaf` asserts
`grand.middles` and `middle.leaves` load after save and a session clear and are
keyed like their to-one side. The hub layout (`PrbHub`, multi-column nested
part between plain parts, `PrbHubChild`) over the 6 permutations asserts the
children load and are keyed like the hub key. The spec now runs 49 iterations.
- `BidirectionalOneToManyLinkerSpec`: two new unit features (the copy is
marked sorted and Hibernate does not permute it again; the to-one side is
aligned once, before the key copies its columns). `CollectionKeyBinderSpec` and
`CollectionSecondPassBinderSpec` use the new constructor.
- `:grails-data-hibernate7-core:test` passes in full (3714 tests, 0
failures, 15 skipped), `checkstyleMain` and `codenarcMain` are clean.
`CompositeForeignKeyColumnTypesSpec` and the existing foreign key and DDL specs
are unchanged and pass.
Fixes #16565
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]