[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ACG4V9WFPVBKFKNN0J2PZR":3,"skill-same:01M6ACG4V9WFPVBKFKNN0J2PZR":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":57,"repositoryHtmlUrl":58,"repositoryHomepageUrl":59,"ownerHtmlUrl":60,"ownerBlogUrl":61,"ownerDescription":6,"updatedAt":62,"install":63,"resources":76},"01M6ACG4V9WFPVBKFKNN0J2PZR","hibernate-developer",null,"Guide for working in the grails-data-hibernate modules (Hibernate 7 on 8.0.x and Hibernate 5 on 7.0.x), covering domain binding, session and transaction lifecycle management across GrailsHibernateTemplate and executeWithNewSession, criteria and SQM subquery correlation, patch-line API minimalism, dialect semantics, and integration testing. Use this when changing code or tests under grails-data-hibernate*.","apache\u002Fgrails-core","apache","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F47359?v=4","01M6AC83E6K4FDKK7HZ8WNMQKE","The Apache Software Foundation","01M6ACFVWFRRTGBQBMWZHV6TS9",2942,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002F.agents\u002Fskills\u002Fhibernate-developer\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"developer","hibernate","hibernate developer","guide","correlation","transaction","use","module","tests","lifecycle","session","for","domain","data","line","when","integration testing","testing","code","grails","2026-11-10 10:12:30",false,"---\nname: hibernate-developer\ndescription: Guide for working in the grails-data-hibernate modules (Hibernate 7 on 8.0.x and Hibernate 5 on 7.0.x), covering domain binding, session and transaction lifecycle management across GrailsHibernateTemplate and executeWithNewSession, criteria and SQM subquery correlation, patch-line API minimalism, dialect semantics, and integration testing. Use this when changing code or tests under grails-data-hibernate*.\nlicense: Apache-2.0\n---\n\u003C!--\nSPDX-License-Identifier: Apache-2.0\n\nLicensed to the Apache Software Foundation (ASF) under one or more contributor license agreements; and to You under the Apache License, Version 2.0. \n-->\n\n## What I Do\n\n- Provide repository-specific guidance for the `grails-data-hibernate7` project on the 8.0.x line.\n- Guide changes around `GrailsDomainBinder`, `GrailsPropertyBinder`, `IdentityBinder`, `VersionBinder`, collection binders, and related utilities.\n- Guide transaction and session lifecycle management across `GrailsHibernateTemplate`, `executeWithNewSession`, and Spring transaction synchronization.\n- Guide criteria translation, Hibernate 7 SQM AST construction, alias scoping, and subquery correlation.\n- Enforce dialect-specific SQL semantics (Oracle, Db2, PostgreSQL, MySQL) and database engine constraints.\n- Keep changes aligned with the testing constraints used by the Hibernate 7 modules in this repository.\n- Help with migration work inside this framework module (e.g., porting domain-binding behaviour from H5 to H7, updating binder internals, fixing H7 regressions). Does not cover user-facing application migration guides; those belong in `grails-doc`.\n\n## When to Use Me\n\nActivate this skill when working on the Hibernate modules, especially for:\n\n- Changes under `grails-data-hibernate7\u002F**` or `grails-data-hibernate5\u002F**`.\n- Session and transaction lifecycle management across `GrailsHibernateTemplate`, `executeWithNewSession`, and `OuterSessionLifecycleStrategy`.\n- Hibernate mapping and metadata binding work.\n- Identifier, version, collection, association, or generator binding changes.\n- Criteria, DetachedCriteria, SQM AST, alias scoping, and subquery correlation.\n- Hibernate regression fixes and migration follow-up tasks.\n- Specs that exercise Hibernate-backed mapping behavior or transaction lifecycle rather than lightweight unit behavior.\n\n## Module Context\n\nThis skill is for the Grails framework's Hibernate 7 integration module on the 8.0.x line (where 8.1.x is the Spring Boot 4.1 line), not for a Grails application. Prefer guidance from this skill over generic Grails app patterns when working in `grails-data-hibernate7`.\n\n`GrailsDomainBinder` is the main entry point for binding Grails domain classes to Hibernate metadata. Changes often ripple through:\n\n- `org.grails.orm.hibernate.cfg`\n- `org.grails.orm.hibernate.cfg.domainbinding`\n- `org.grails.orm.hibernate.cfg.domainbinding.collectionType`\n- `org.grails.orm.hibernate.cfg.domainbinding.secondpass`\n- `org.grails.orm.hibernate.cfg.domainbinding.generator`\n\n## Key Classes and Responsibilities\n\n### Main Binding Flow\n\n- `GrailsDomainBinder`: central coordinator for Hibernate 7 mapping contribution.\n- `GrailsPropertyBinder`: main coordinator for converting persistent properties into Hibernate `Value` instances.\n- `PropertyFromValueCreator`: shared utility for creating Hibernate `Property` instances from a bound `Value`.\n\n### Identifier and Version Binding\n\n- `IdentityBinder`: coordinates identifier binding.\n- `SimpleIdBinder`: handles simple identifiers.\n- `CompositeIdBinder`: handles composite identifiers.\n- `VersionBinder`: binds optimistic locking version properties.\n- `NaturalIdentifierBinder`: binds `naturalId` properties.\n\n### Associations and Collections\n\n- `OneToOneBinder`, `ManyToOneBinder`, `ManyToOneValuesBinder`: association binding.\n- `CollectionBinder`: collection mapping.\n- `CollectionSecondPassBinder`, `ListSecondPassBinder`, `MapSecondPassBinder`: second-pass association and collection binding.\n- `CollectionHolder` plus the collection type classes: carry collection metadata through binding.\n\n### Value and Column Binding\n\n- `SimpleValueBinder`: binds simple properties.\n- `SimpleValueColumnBinder`: binds columns to simple values.\n- `ComponentBinder`, `ComponentPropertyBinder`: embedded\u002Fcomponent binding.\n- `CompositeUserTypeBinder`: binds a property mapped with a Hibernate `CompositeUserType` as a `Component` (one column per attribute of the user type's `embeddable()` class, in alphabetical attribute order, with `returnedClass()` as the component class); a `BasicValue` cannot carry a composite user type. The attributes and the column of each one come from `CompositeUserTypeColumns` (in `domainbinding.util`), which `ColumnNameForPropertyAndPathFetcher.getColumnNamesForPropertyAndPath` also uses, so a uniqueness group that names such a property spans all of its columns. Its columns are bound with `ColumnBinder.bindColumnWithoutUniqueness`, which takes the type of the attribute and defines the column from its own configuration (the constraints of the property describe the whole value; `PropertyConfig.getScale()` and friends throw on a multi-column property), and its uniqueness with one `CreateKeyForProps.createKeyForColumns` key across the bound `Column` objects themselves (which keeps their quoting), never a unique key per column. The user type is instantiated only through `CompositeUserTypeColumns.userTypeOf`, which is CDI-aware when it has the `MetadataBuildingContext`, so pass the context to `ColumnNameForPropertyAndPathFetcher`, `ColumnBinder` and `CompositeUserTypeColumns` wherever it is available. `GrailsPropertyBinder` checks for a composite user type before the enum branch, since an enum property may map one.\n- `EnumTypeBinder`: enum mapping.\n\n### Generators\n\n- `BasicValueCreator`: creates identifier values and generators.\n- `GrailsSequenceWrapper`, `GrailsSequenceGeneratorEnum`: generator integration helpers.\n- `GrailsIdentityGenerator`, `GrailsIncrementGenerator`, `GrailsNativeGenerator`, `GrailsSequenceStyleGenerator`, `GrailsTableGenerator`: Grails-specific Hibernate 7 generator implementations.\n\n## Current Module Guidance\n\nKeep these module-specific expectations in mind:\n\n- `GrailsPropertyBinder` has already been simplified to a unified binder-dispatch structure. Preserve that consolidation instead of reintroducing scattered property creation or ad hoc branching.\n- Property creation and addition should stay centralized through callers using `PropertyFromValueCreator` where applicable.\n- 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.\n- `GrailsIncrementGenerator` still contains reflection-based Hibernate 7 compatibility workarounds; avoid broad refactors unless the change explicitly addresses that area.\n\n## Transaction and Session Lifecycle Management\n\nFramework 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:\n\n- **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.\n- **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.\n- **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.\n- **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.\n- **Suspend and restore closed bound holders**: When a `SessionHolder` is bound to the thread in Spring's `TransactionSynchronizationManager`, it must be suspended and restored across `withNewSession` even if its underlying session is closed, preventing `IllegalStateException: Already value bound to thread`.\n- **Translate post-callback flush exceptions**: Call `flushIfNecessary` inside the exception-translation block (`executeInSession`) so Hibernate flush exceptions (e.g. database constraint overruns) are converted to Spring's `DataAccessException` hierarchy (`DataIntegrityViolationException`) rather than escaping unconverted.\n- **Strict patch-line API minimalism**: On maintenance and patch branches (`7.0.x` or `8.0.x`), never introduce new public or protected methods, classes, or extension points. Never introduce fields or internal state that are assigned but never read. Keep bugfix diffs strictly minimal.\n- **Avoid test-driven production pollution**: Do not add multi-argument constructor or method overloads to production classes solely to facilitate unit-test mocks; test behavior through real entity execution or pass necessary runtime flags directly.\n- **Authentic exception testing**: Exercise real database\u002FORM constraint overruns (e.g. column length violation) in tests rather than throwing mock `HibernateException` instances directly, verifying real exception conversion paths.\n\n## Criteria, SQM AST, and Subquery Correlation\n\nHibernate 7 compiles Criteria into an internal Semantic Query Model (SQM) AST. When working with DetachedCriteria, subqueries, and criteria builders:\n\n- **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.\n- **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.\n- **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.\n\n## Dialect Matrix and Database Engine Differences\n\nDo not assume H2 or default SQL semantics apply across all supported database engines:\n\n- **Unique index null semantics**:\n  - **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`.\n  - **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.\n  - **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.\n  - **NoSQL \u002F Neo4j**: A null unique association represents the non-existence of a relationship. Never match null unique associations against nodes that have existing relationships.\n- **Shared join tables**:\n  - 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.\n  - Calling `clear()` or bulk-deleting one association on a shared join table with separate key columns must leave the rows of the other association intact.\n\n## Mandatory Invariant Verification Protocol\n\nBefore submitting changes in Hibernate, transaction, or GORM layers, you must evaluate and document the **Invariant Matrix**:\n\n1. **Transaction Matrix**:\n   - `PROPAGATION_REQUIRED`: uncommitted writes flushed on commit, no premature disconnect.\n   - `PROPAGATION_REQUIRES_NEW`: inner transaction isolated from outer transaction.\n   - `PROPAGATION_SUPPORTS` (read-only): outer session remains usable after inner block without connection leaks.\n   - Direct `DataSource` vs `LazyConnectionDataSourceProxy`: verify no eager physical connection checkout when session is idle.\n2. **Adversarial Audit Questions**:\n   - *Does this disconnect an active transaction that has uncommitted dirty entities?*\n   - *Does this allocate a pooled connection if the session never executes a query?*\n   - *What does the physical database index do when composite keys are all-null vs partially-null, and do sets on shared join tables use one combined key or a key per association?*\n   - *What happens when two sibling closures in a criteria query join the same property name?*\n\n## Testing Rules\n\nWhen touching `grails-data-hibernate7`, test through real Hibernate wiring rather than assuming mocks are enough.\n\n- Use `HibernateGormDatastoreSpec` for Hibernate 7 integration and domain-binding specifications.\n- Prefer `manager.registerDomainClasses(...)` in `setupSpec()` to register entities for specs.\n- Define test entities as top-level classes in the same Groovy spec file.\n- Ensure test domain class names are globally unique within the package. The test suite uses `maxParallelForks > 1`, so multiple specs can run concurrently in the same JVM fork. `HibernateDatastore` caches mapping metadata by entity class name, so two specs registering a domain class with the same simple name in the same package can overwrite each other's mappings and cause flaky failures.\n- Prefer real entities over heavy mocking for binder logic.\n\n## Change Workflow\n\n1. Identify which binder, creator, generator, fetcher, or second-pass class owns the behavior.\n2. Trace whether the change affects only `Value` creation, `Property` creation, or both.\n3. Preserve the existing separation between logical mapping decisions and Hibernate object construction.\n4. Update or add specs in `grails-data-hibernate7` that exercise the affected behavior through the public Hibernate-backed path.\n5. Run the relevant Hibernate 7 module tests, and expand test coverage when binder flow or entity registration behavior changes.\n\n## Pitfalls to Avoid\n\n- Do not treat this module like a simple Grails application layer; it is framework and mapping infrastructure code.\n- Do not reintroduce duplicated property-creation logic if a shared binder or creator already owns it.\n- Do not rely on unit-only mocking for Hibernate internals when the behavior depends on real metadata binding.\n- Do not use nested or inner entity classes in Hibernate 7 specs when top-level classes are required for AST transforms and reliable registration.\n\n## Known Status and Constraints\n\n- The Hibernate 7 binder migration is complete: all main binders, collection types, second-pass binders, generators, and utilities have been migrated.\n- `GrailsIncrementGenerator` retains reflection-based workarounds for accessing Hibernate 7 internals; avoid broad refactors in that class unless explicitly targeting that area.\n\n## Source of Truth\n\nThis skill is the repository guidance for Hibernate 7 module work. When module conventions change, update this skill directly so agents load the current rules from `.agents\u002Fskills\u002Fhibernate-developer\u002FSKILL.md`.\n","\u003C!--\nSPDX-License-Identifier: Apache-2.0\n\nLicensed to the Apache Software Foundation (ASF) under one or more contributor license agreements; and to You under the Apache License, Version 2.0. \n-->\n\n## What I Do\n\n- Provide repository-specific guidance for the `grails-data-hibernate7` project on the 8.0.x line.\n- Guide changes around `GrailsDomainBinder`, `GrailsPropertyBinder`, `IdentityBinder`, `VersionBinder`, collection binders, and related utilities.\n- Guide transaction and session lifecycle management across `GrailsHibernateTemplate`, `executeWithNewSession`, and Spring transaction synchronization.\n- Guide criteria translation, Hibernate 7 SQM AST construction, alias scoping, and subquery correlation.\n- Enforce dialect-specific SQL semantics (Oracle, Db2, PostgreSQL, MySQL) and database engine constraints.\n- Keep changes aligned with the testing constraints used by the Hibernate 7 modules in this repository.\n- Help with migration work inside this framework module (e.g., porting domain-binding behaviour from H5 to H7, updating binder internals, fixing H7 regressions). Does not cover user-facing application migration guides; those belong in `grails-doc`.\n\n## When to Use Me\n\nActivate this skill when working on the Hibernate modules, especially for:\n\n- Changes under `grails-data-hibernate7\u002F**` or `grails-data-hibernate5\u002F**`.\n- Session and transaction lifecycle management across `GrailsHibernateTemplate`, `executeWithNewSession`, and `OuterSessionLifecycleStrategy`.\n- Hibernate mapping and metadata binding work.\n- Identifier, version, collection, association, or generator binding changes.\n- Criteria, DetachedCriteria, SQM AST, alias scoping, and subquery correlation.\n- Hibernate regression fixes and migration follow-up tasks.\n- Specs that exercise Hibernate-backed mapping behavior or transaction lifecycle rather than lightweight unit behavior.\n\n## Module Context\n\nThis skill is for the Grails framework's Hibernate 7 integration module on the 8.0.x line (where 8.1.x is the Spring Boot 4.1 line), not for a Grails application. Prefer guidance from this skill over generic Grails app patterns when working in `grails-data-hibernate7`.\n\n`GrailsDomainBinder` is the main entry point for binding Grails domain classes to Hibernate metadata. Changes often ripple through:\n\n- `org.grails.orm.hibernate.cfg`\n- `org.grails.orm.hibernate.cfg.domainbinding`\n- `org.grails.orm.hibernate.cfg.domainbinding.collectionType`\n- `org.grails.orm.hibernate.cfg.domainbinding.secondpass`\n- `org.grails.orm.hibernate.cfg.domainbinding.generator`\n\n## Key Classes and Responsibilities\n\n### Main Binding Flow\n\n- `GrailsDomainBinder`: central coordinator for Hibernate 7 mapping contribution.\n- `GrailsPropertyBinder`: main coordinator for converting persistent properties into Hibernate `Value` instances.\n- `PropertyFromValueCreator`: shared utility for creating Hibernate `Property` instances from a bound `Value`.\n\n### Identifier and Version Binding\n\n- `IdentityBinder`: coordinates identifier binding.\n- `SimpleIdBinder`: handles simple identifiers.\n- `CompositeIdBinder`: handles composite identifiers.\n- `VersionBinder`: binds optimistic locking version properties.\n- `NaturalIdentifierBinder`: binds `naturalId` properties.\n\n### Associations and Collections\n\n- `OneToOneBinder`, `ManyToOneBinder`, `ManyToOneValuesBinder`: association binding.\n- `CollectionBinder`: collection mapping.\n- `CollectionSecondPassBinder`, `ListSecondPassBinder`, `MapSecondPassBinder`: second-pass association and collection binding.\n- `CollectionHolder` plus the collection type classes: carry collection metadata through binding.\n\n### Value and Column Binding\n\n- `SimpleValueBinder`: binds simple properties.\n- `SimpleValueColumnBinder`: binds columns to simple values.\n- `ComponentBinder`, `ComponentPropertyBinder`: embedded\u002Fcomponent binding.\n- `CompositeUserTypeBinder`: binds a property mapped with a Hibernate `CompositeUserType` as a `Component` (one column per attribute of the user type's `embeddable()` class, in alphabetical attribute order, with `returnedClass()` as the component class); a `BasicValue` cannot carry a composite user type. The attributes and the column of each one come from `CompositeUserTypeColumns` (in `domainbinding.util`), which `ColumnNameForPropertyAndPathFetcher.getColumnNamesForPropertyAndPath` also uses, so a uniqueness group that names such a property spans all of its columns. Its columns are bound with `ColumnBinder.bindColumnWithoutUniqueness`, which takes the type of the attribute and defines the column from its own configuration (the constraints of the property describe the whole value; `PropertyConfig.getScale()` and friends throw on a multi-column property), and its uniqueness with one `CreateKeyForProps.createKeyForColumns` key across the bound `Column` objects themselves (which keeps their quoting), never a unique key per column. The user type is instantiated only through `CompositeUserTypeColumns.userTypeOf`, which is CDI-aware when it has the `MetadataBuildingContext`, so pass the context to `ColumnNameForPropertyAndPathFetcher`, `ColumnBinder` and `CompositeUserTypeColumns` wherever it is available. `GrailsPropertyBinder` checks for a composite user type before the enum branch, since an enum property may map one.\n- `EnumTypeBinder`: enum mapping.\n\n### Generators\n\n- `BasicValueCreator`: creates identifier values and generators.\n- `GrailsSequenceWrapper`, `GrailsSequenceGeneratorEnum`: generator integration helpers.\n- `GrailsIdentityGenerator`, `GrailsIncrementGenerator`, `GrailsNativeGenerator`, `GrailsSequenceStyleGenerator`, `GrailsTableGenerator`: Grails-specific Hibernate 7 generator implementations.\n\n## Current Module Guidance\n\nKeep these module-specific expectations in mind:\n\n- `GrailsPropertyBinder` has already been simplified to a unified binder-dispatch structure. Preserve that consolidation instead of reintroducing scattered property creation or ad hoc branching.\n- Property creation and addition should stay centralized through callers using `PropertyFromValueCreator` where applicable.\n- 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.\n- `GrailsIncrementGenerator` still contains reflection-based Hibernate 7 compatibility workarounds; avoid broad refactors unless the change explicitly addresses that area.\n\n## Transaction and Session Lifecycle Management\n\nFramework 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:\n\n- **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.\n- **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.\n- **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.\n- **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.\n- **Suspend and restore closed bound holders**: When a `SessionHolder` is bound to the thread in Spring's `TransactionSynchronizationManager`, it must be suspended and restored across `withNewSession` even if its underlying session is closed, preventing `IllegalStateException: Already value bound to thread`.\n- **Translate post-callback flush exceptions**: Call `flushIfNecessary` inside the exception-translation block (`executeInSession`) so Hibernate flush exceptions (e.g. database constraint overruns) are converted to Spring's `DataAccessException` hierarchy (`DataIntegrityViolationException`) rather than escaping unconverted.\n- **Strict patch-line API minimalism**: On maintenance and patch branches (`7.0.x` or `8.0.x`), never introduce new public or protected methods, classes, or extension points. Never introduce fields or internal state that are assigned but never read. Keep bugfix diffs strictly minimal.\n- **Avoid test-driven production pollution**: Do not add multi-argument constructor or method overloads to production classes solely to facilitate unit-test mocks; test behavior through real entity execution or pass necessary runtime flags directly.\n- **Authentic exception testing**: Exercise real database\u002FORM constraint overruns (e.g. column length violation) in tests rather than throwing mock `HibernateException` instances directly, verifying real exception conversion paths.\n\n## Criteria, SQM AST, and Subquery Correlation\n\nHibernate 7 compiles Criteria into an internal Semantic Query Model (SQM) AST. When working with DetachedCriteria, subqueries, and criteria builders:\n\n- **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.\n- **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.\n- **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.\n\n## Dialect Matrix and Database Engine Differences\n\nDo not assume H2 or default SQL semantics apply across all supported database engines:\n\n- **Unique index null semantics**:\n  - **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`.\n  - **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.\n  - **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.\n  - **NoSQL \u002F Neo4j**: A null unique association represents the non-existence of a relationship. Never match null unique associations against nodes that have existing relationships.\n- **Shared join tables**:\n  - 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.\n  - Calling `clear()` or bulk-deleting one association on a shared join table with separate key columns must leave the rows of the other association intact.\n\n## Mandatory Invariant Verification Protocol\n\nBefore submitting changes in Hibernate, transaction, or GORM layers, you must evaluate and document the **Invariant Matrix**:\n\n1. **Transaction Matrix**:\n   - `PROPAGATION_REQUIRED`: uncommitted writes flushed on commit, no premature disconnect.\n   - `PROPAGATION_REQUIRES_NEW`: inner transaction isolated from outer transaction.\n   - `PROPAGATION_SUPPORTS` (read-only): outer session remains usable after inner block without connection leaks.\n   - Direct `DataSource` vs `LazyConnectionDataSourceProxy`: verify no eager physical connection checkout when session is idle.\n2. **Adversarial Audit Questions**:\n   - *Does this disconnect an active transaction that has uncommitted dirty entities?*\n   - *Does this allocate a pooled connection if the session never executes a query?*\n   - *What does the physical database index do when composite keys are all-null vs partially-null, and do sets on shared join tables use one combined key or a key per association?*\n   - *What happens when two sibling closures in a criteria query join the same property name?*\n\n## Testing Rules\n\nWhen touching `grails-data-hibernate7`, test through real Hibernate wiring rather than assuming mocks are enough.\n\n- Use `HibernateGormDatastoreSpec` for Hibernate 7 integration and domain-binding specifications.\n- Prefer `manager.registerDomainClasses(...)` in `setupSpec()` to register entities for specs.\n- Define test entities as top-level classes in the same Groovy spec file.\n- Ensure test domain class names are globally unique within the package. The test suite uses `maxParallelForks > 1`, so multiple specs can run concurrently in the same JVM fork. `HibernateDatastore` caches mapping metadata by entity class name, so two specs registering a domain class with the same simple name in the same package can overwrite each other's mappings and cause flaky failures.\n- Prefer real entities over heavy mocking for binder logic.\n\n## Change Workflow\n\n1. Identify which binder, creator, generator, fetcher, or second-pass class owns the behavior.\n2. Trace whether the change affects only `Value` creation, `Property` creation, or both.\n3. Preserve the existing separation between logical mapping decisions and Hibernate object construction.\n4. Update or add specs in `grails-data-hibernate7` that exercise the affected behavior through the public Hibernate-backed path.\n5. Run the relevant Hibernate 7 module tests, and expand test coverage when binder flow or entity registration behavior changes.\n\n## Pitfalls to Avoid\n\n- Do not treat this module like a simple Grails application layer; it is framework and mapping infrastructure code.\n- Do not reintroduce duplicated property-creation logic if a shared binder or creator already owns it.\n- Do not rely on unit-only mocking for Hibernate internals when the behavior depends on real metadata binding.\n- Do not use nested or inner entity classes in Hibernate 7 specs when top-level classes are required for AST transforms and reliable registration.\n\n## Known Status and Constraints\n\n- The Hibernate 7 binder migration is complete: all main binders, collection types, second-pass binders, generators, and utilities have been migrated.\n- `GrailsIncrementGenerator` retains reflection-based workarounds for accessing Hibernate 7 internals; avoid broad refactors in that class unless explicitly targeting that area.\n\n## Source of Truth\n\nThis skill is the repository guidance for Hibernate 7 module work. When module conventions change, update this skill directly so agents load the current rules from `.agents\u002Fskills\u002Fhibernate-developer\u002FSKILL.md`.\n",{"name":5,"license":15,"description":7},"8.0.x","88272db2f39e7254a950d4aacad03aedc545337c",".agents\u002Fskills\u002Fhibernate-developer",979,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core","https:\u002F\u002Fgrails.apache.org\u002F","https:\u002F\u002Fgithub.com\u002Fapache","https:\u002F\u002Fwww.apache.org\u002F","2026-11-14 05:52:37",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add apache\u002Fgrails-core --skill hibernate-developer",{"tool":69,"command":70},"bunx","bunx skills add apache\u002Fgrails-core --skill hibernate-developer",{"tool":72,"command":73},"pnpm","pnpm dlx skills add apache\u002Fgrails-core --skill hibernate-developer","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002F.agents\u002Fskills\u002Fhibernate-developer\u002FSKILL.md",true,[],[78,104,120,128,136,142],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":49},"01M6ACGQCDSQFZRCVK2K0M784K","grails-8-upgrade","Guide for upgrading Grails applications from Grails 7.x to Grails 8, covering Java 21, Groovy 5 name resolution and static compilation, Spring Boot 4.1, Spring Framework 7, dependency management, Micronaut, Jackson 3, Hibernate 7, TagLibs, testing, content negotiation, asset pipeline wildcard paths, and validation behavior changes","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002Fgrails-skills\u002Fupgrade-guide-8\u002Fskills\u002Fgrails-8-upgrade\u002FSKILL.md",[],[48,85,86,87,88,89,90,91,46,92,93,94,95,96,97,98,99,100,40,32,101,102,103],"grails 8 upgrade","upgrade","framework","management","behavior","groovy","asset pipeline","validation","boot","java","name","changes","content","spring","applications","dependency management","paths","spring framework","static",{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":112,"publishedAt":49},"01M6ACGMW980S58X7861TC896N","grails-developer","Comprehensive guide for Grails development, covering web applications, REST APIs, GORM, controllers, services, views, plugins, and testing with Spock and Geb","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002Fgrails-skills\u002Fdeveloper\u002Fskills\u002Fgrails-developer\u002FSKILL.md",[110,111],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[29,113,48,32,114,115,116,46,40,99,117,118,119],"grails developer","plugins","web","development","with","REST","services",{"skillNo":121,"name":122,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":126,"publishedAt":49},"01M6ACGJSZHJ51G04AJ0QMMQNS","java-developer","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002F.claude\u002Fskills\u002Fjava-developer\u002FSKILL.md",[125],{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[29,94,127],"java developer",{"skillNo":129,"name":130,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":134,"publishedAt":49},"01M6ACGHERQ26NGCVEJ9QB9736","groovy-developer","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002F.claude\u002Fskills\u002Fgroovy-developer\u002FSKILL.md",[133],{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[29,135,90],"groovy developer",{"skillNo":137,"name":106,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":138,"categories":139,"keywords":141,"publishedAt":49},"01M6ACGG1WM67J8YC5CWNTFSHM","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002F.claude\u002Fskills\u002Fgrails-developer\u002FSKILL.md",[140],{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[29,113,48],{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":148,"publishedAt":49},"01M6ACGDZ4Z6FRBRVZFYFF29FW","violation-fixer","Guide for running, interpreting, and fixing code style and analysis violations in grails-core using GrailsCodeStylePlugin, GrailsCodeAnalysisPlugin, and GrailsViolationAggregationPlugin - covering CodeNarc, Checkstyle, PMD, SpotBugs, and JaCoCo","https:\u002F\u002Fgithub.com\u002Fapache\u002Fgrails-core\u002Fblob\u002F8.0.x\u002F.agents\u002Fskills\u002Fviolation-fixer\u002FSKILL.md",[],[149,150,32,48,151,47,152,153,154,40,155,156,157,158],"violation fixer","fixer","core","code style","running","spotbugs","analysis","checkstyle","style","pmd"]