jdaugherty commented on code in PR #16028:
URL: https://github.com/apache/grails-core/pull/16028#discussion_r3824641343
##########
grails-doc/src/en/guide/upgrading/upgrading80x.adoc:
##########
@@ -1215,6 +1215,60 @@ GORM's `createCriteria()` and `withCriteria()` DSL are
implemented on top of the
*`javax.persistence` → `jakarta.persistence`*: This migration was already
required for Grails 7; Grails 8 continues to require `jakarta.*`.
+[[_join_table_foreign_key_column_names]]
+===== 26.9 Join-Table Foreign-Key Column Names
+
+This change affects only a *unidirectional* `hasMany` — a collection with no
`belongsTo` and no reciprocal `hasMany` on the other side. A *bidirectional*
many-to-many association (both sides declare `hasMany`, or one side uses
`belongsTo`) is **not** affected: both of its join-table foreign-key columns
are still derived from the class names, identical to Grails 7.
+
+In Grails 7 (Hibernate 5), the default foreign-key column that referenced the
associated entity in a unidirectional `hasMany` join table was derived from the
simple name of that entity's domain class, after applying the column naming
strategy.
+For example, the domain class `Book` produced the foreign-key column
`book_id`, even when its physical table was mapped to a different name.
+
+In Grails 8 (Hibernate 7), that foreign-key column is instead derived from the
physical table name of the associated domain class.
+The physical name includes an explicit `table` mapping and any transformation
made by a custom physical naming strategy.
+For example, given the following mapping:
+
+[source,groovy]
+----
+class Book {
+ static mapping = {
+ table 'catalog_book'
+ }
+}
+
+class Shelf {
+ String label
+
+ static hasMany = [books: Book] // unidirectional: no belongsTo, no
reciprocal hasMany
+}
+----
+
+Grails 7 used `book_id` by default in the `shelf_books` join table, whereas
Grails 8 uses `catalog_book_id`. The other column in that same join table
(`shelf_id`, derived from the owning `Shelf` class) is unchanged.
+This is a breaking schema change for an existing database if its join table
still uses the Grails 7 column name.
+
+To keep the existing schema unchanged, configure the join table and its
foreign-key column explicitly in the `static mapping` block:
+
+[source,groovy]
+----
+class Shelf {
+ String label
+
+ static hasMany = [books: Book]
+
+ static mapping = {
+ books joinTable: [
+ name: 'shelf_books',
+ key: 'shelf_id',
+ column: 'book_id'
+ ]
+ }
+}
+----
+
+Replace `shelf_books`, `shelf_id`, and `book_id` with the table and column
names already used by your database.
+Declaring all three names prevents the naming strategy from changing the
mapping during the upgrade.
+
+Separately, the element column of a *basic or enum collection* (e.g. `static
hasMany = [items: String]`) is now resolved through the naming strategy's
*column* naming rules instead of its *table* naming rules for the property-name
prefix (e.g. `items_value`). Most naming strategies apply the same
transformation to both, so this only matters if your custom
`PhysicalNamingStrategy` implements `toPhysicalColumnName` and
`toPhysicalTableName` differently.
Review Comment:
Two accuracy issues in this paragraph:
1. An enum collection is not affected by this change. The `resolveTableName`
→ `resolveColumnName` switch only changed the property-name prefix, and an enum
element column has no property-name prefix —
`HibernateToManyProperty#joinTableColumName` returns just the element-type
identifier for enums (`referencedType.isEnum() ? clazz : ...`), and that
identifier already went through `resolveColumnName` before this PR. So this
should say *basic (non-enum) collection*.
2. `items_value` doesn't match what the code derives. The default element
column is `<property>_<element-type identifier>` (which is exactly why the new
spec asserts a `tags_as_column_` *prefix* rather than a full name), so
`items_value` reads like a real default when it isn't. Suggest either showing
the actual derived name for the example mapping or describing the pattern
instead of naming a column.
##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateAssociation.java:
##########
@@ -80,6 +82,15 @@ default String getReferencedEntityName() {
return getHibernateAssociatedEntity().getName();
}
+ default String
resolveAssociatedEntityTableName(PersistentEntityNamingStrategy namingStrategy)
{
Review Comment:
nit: after the latest revision this method has exactly one caller
(`HibernateToManyProperty#resolveJoinTableForeignKeyColumnName`) — the
basic-collection element path no longer resolves through it, so "Every caller …
(a join-table foreign-key or element column name)" overstates it. Consider
trimming the comment to the foreign-key case, and possibly moving the method to
`HibernateToManyProperty` next to its only caller so it doesn't suggest the
ToOne side uses it too.
--
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]