borinquenkid opened a new issue, #16699:
URL: https://github.com/apache/grails-core/issues/16699
### Expected Behavior
An owner with two unidirectional `hasMany` to the same class that share one
join table (no `joinTable` mapping)
```groovy
class Item {
static hasMany = [roles: Role, denyRoles: Role]
}
```
writes a role added to either collection to the join table, or fails the
save with an error. `failOnError: true` must not pass while rows are dropped.
### Actual Behaviour
On Hibernate 7 the classic domain binder keeps the rule from #10811 in
`CollectionKeyColumnUpdater`: when the owner has more than one unidirectional
to-many sharing the collection's join table, the key of each collection is made
nullable **and not updatable**. Hibernate 7's collection persister writes no
rows for a collection whose key is not updatable, so `addToRoles` and
`addToDenyRoles` are both dropped silently and the collections reload empty,
even with `failOnError: true`.
#16686 keeps the key updatable for collections with join tables of their own
(the reporter's first shape in #16665) and leaves this shared-table case as it
was. This issue is the follow-up it points to.
### Analysis
- A local probe by @jdaugherty on #16686 made the key always updatable: both
collections of `hasMany = [roles: Role, denyRoles: Role]` were written to the
shared table and read back correctly, and the `hasmany` package and the specs
of #16686 still passed. So the #10811 rule may not be needed on Hibernate 7 at
all, as the reporter suggested in #16665.
- The native domain generator of the 9.x branches already treats the rule as
a defect rather than a rule to copy and always makes the key updatable (see the
javadoc of `GrailsDomainGenerator.collectionKeyFacets`), so dropping it on 8.x
brings the classic binder in line with native binding.
- What needs a look before dropping it: the shared join table has three
columns (`item_id`, `item_roles_id`, `item_deny_roles_id`) but the classic
binder's unique key covers only the **first** collection's key column with the
element column, `UNIQUE (item_roles_id, item_id)`. Rows of the second
collection leave `item_roles_id` null. H2 creates the key `NULLS DISTINCT`, but
databases that treat nulls as equal in a unique constraint (SQL Server, for
example) would reject two `denyRoles` rows for the same role from different
owners. Either the unique key has to cover every key column of the table, or
each collection needs a unique key of its own, or the binder should give each
collection its own join table by default.
### Steps To Reproduce
Register the `Item`/`Role` classes above against the Hibernate 7 datastore
of 8.0.x, add a role to each collection, flush, clear the session and reload:
both collections are empty. `MultipleUnidirectionalHasManySpec` (from #16686)
has the per-collection join table shape; the shared-table shape is
`CKCUSharedTableOwner` in `CollectionKeyColumnUpdaterSpec`, which currently
asserts the key is not updatable.
### Environment Information
- Grails 8.0.x, `grails-data-hibernate7`, classic domain binding.
Related: #16665, #16686, #10811.
--
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]