jamesfredley commented on code in PR #16790:
URL: https://github.com/apache/grails-core/pull/16790#discussion_r4408533777


##########
.agents/skills/hibernate-developer/SKILL.md:
##########
@@ -84,6 +87,51 @@ Keep these module-specific expectations in mind:
 - Utility classes in `domainbinding.util` should prefer Hibernate-aware GORM 
types internally, but public signatures may still need base interfaces when 
Spock mocks require them.
 - `GrailsIncrementGenerator` still contains reflection-based Hibernate 7 
compatibility workarounds; avoid broad refactors unless the change explicitly 
addresses that area.
 
+## Transaction and Session Lifecycle Management
+
+Framework code touching `GrailsHibernateTemplate`, `executeWithNewSession`, or 
session binding operates at the collision point of Spring's transaction 
synchronization, Hibernate's logical connection lifecycle, and the JDBC 
connection pool. Follow these non-negotiable rules:
+
+- **Never disconnect active write-bearing transactions**: Disconnecting an 
outer session in `beforeCompletion` drops uncommitted entity dirty checks and 
pending flushes. The outer transaction reports commit success, but rows are 
silently lost. Only disconnect an outer session if it is an idle or read-only 
`PROPAGATION_SUPPORTS` context that owns no active transaction.
+- **Prevent pool starvation on non-lazy data sources**: Calling connection 
probes (such as `con.getAutoCommit()` or `con.getMetaData()`) on a datasource 
that is *not* wrapped in `LazyConnectionDataSourceProxy` immediately checks out 
a physical connection from the pool. If the outer session is idle and the pool 
is small, this starves the pool and deadlocks inner sessions.
+- **Save and restore connection holders and synchronizations symmetrically**: 
In `executeWithNewSession`, capture the previous `ConnectionHolder` and Spring 
`TransactionSynchronization` list before rebinding, and strictly restore them 
in a `finally` block.
+- **Coordinate with `SESSION_SYNCHRONIZATION_ORDER`**: When registering 
synchronizations with Spring's `TransactionSynchronizationManager`, ensure the 
logical connection disconnects before Spring's `ConnectionSynchronization` 
closes the physical connection, without interfering with active flushes.
+
+## Criteria, SQM AST, and Subquery Correlation
+
+Hibernate 7 compiles Criteria into an internal Semantic Query Model (SQM) AST. 
When working with DetachedCriteria, subqueries, and criteria builders:
+
+- **Lexical alias scoping**: Materialize nested aliases strictly on the 
association scope that declares them. Never fall back to implicit parent 
aliases or materialize them onto the query root.
+- **Sibling association block isolation**: Sibling association blocks querying 
the same association name must maintain independent joins rather than 
collapsing into a shared join that leaks predicates between blocks.
+- **Subquery correlation over inherited joins**: When correlating a subquery 
to an outer query through nested or inherited joins, ensure the outer join path 
remains accessible to the subquery predicates without dropping the comparison 
or creating redundant inner joins.
+
+## Dialect Matrix and Database Engine Differences
+
+Do not assume H2 or default SQL semantics apply across all supported database 
engines:
+
+- **Unique index null semantics**:
+  - **Single property `unique: true, nullable: true`**: A `null` value is 
always exempt from uniqueness checks across all datastores and in-memory test 
environments. Never execute a uniqueness query when the target value is `null`.
+  - **Oracle B-Tree index**: Oracle omits index entries where *every* column 
of the unique index is `null`. Multiple all-null rows are accepted by the 
database. If even one column in a composite unique key is non-null, Oracle 
indexes it and enforces uniqueness.
+  - **IBM Db2**: In Hibernate 7, `DB2Dialect` appends `exclude null keys` for 
LUW, while `DB2iDialect` and `DB2zDialect` render `create unique where not null 
index`. Do not force GORM `value` comparisons on all-null rows that the 
database dialect omits.
+  - **NoSQL / Neo4j**: A null unique association represents the non-existence 
of a relationship. Never match null unique associations against nodes that have 
existing relationships.
+- **Shared join tables**:
+  - Sets receive unique keys per association, lists and maps are indexed, and 
bags have no key.

Review Comment:
   Sets on a dialect that compares nulls, Oracle and Db2 LUW, share one unique 
key across the associations. A separate key per association rejects two valid 
rows that are null in one association's columns and equal in the element. 
Dialects that treat nulls as distinct keep a separate key per association. 
Lists and maps stay indexed, and bags stay without a key.



-- 
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