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]

Reply via email to