Skip to content

fix: handle null argument in GORM domain class constructor (Groovy 4 regression)#15482

Closed
davydotcom wants to merge 1 commit into7.0.xfrom
fix/null-constructor-arg-groovy4
Closed

fix: handle null argument in GORM domain class constructor (Groovy 4 regression)#15482
davydotcom wants to merge 1 commit into7.0.xfrom
fix/null-constructor-arg-groovy4

Conversation

@davydotcom
Copy link
Contributor

Problem

Calling new DomainClass(null) on a GORM domain class throws:

groovy.lang.GroovyRuntimeException: Could not find matching constructor for: DomainClass(null)

This is a regression from Grails 6 / Groovy 3, where new DomainClass(null) was treated equivalently to new DomainClass().

Root Cause

In Groovy 3, the runtime resolved new SomeClass(null) by matching it to the implicit map-based constructor that Groovy provides for POGOs. When null was passed, the runtime simply created the object via the no-arg constructor without setting any properties.

Groovy 4 changed how constructor resolution works at runtime. The implicit map constructor is no longer matched when null is passed as the argument, causing the GroovyRuntimeException.

This is a common pattern in Grails applications — for example, when a variable that may be null is passed to a domain constructor:

def props = maybeGetProperties() // could return null
def obj = new MyDomain(props)    // fails in Grails 7 / Groovy 4

Fix

The GormEntityTransformation (the @Entity AST transform) now injects an explicit Map constructor into every GORM entity class. The constructor:

  • Accepts a Map parameter, allowing null to match at runtime
  • If the map is not null, sets properties via InvokerHelper.setProperties(this, args) — the same mechanism Groovy uses internally for map constructors
  • If null, does nothing (equivalent to the no-arg constructor)
  • Also ensures a no-arg constructor is preserved, since adding any explicit constructor prevents Groovy from auto-generating the default one

The constructor is injected early in the transformation (before trait composition), so trait \$init\$ methods are properly wired into it by the Groovy trait composer.

Tests

Added NullConstructorArgSpec in grails-data-hibernate5/core with three tests:

  1. No-arg constructor works (baseline)
  2. Null constructor argument works the same as no-arg
  3. Domain created with null constructor argument can be saved

All 598 existing hibernate5 core tests continue to pass with 0 failures.

Co-Authored-By: Oz oz-agent@warp.dev

…regression)

In Groovy 3, calling new DomainClass(null) was resolved by the runtime
to the implicit map-based constructor and treated equivalently to
new DomainClass(). Groovy 4 changed how constructor resolution works at
runtime and no longer matches a null argument to the implicit map
constructor, resulting in:

  GroovyRuntimeException: Could not find matching constructor for: DomainClass(null)

This commit injects an explicit Map constructor into GORM entity classes
via GormEntityTransformation. The constructor handles null gracefully by
skipping property assignment, making it behave identically to the no-arg
constructor. A corresponding no-arg constructor is also ensured since
adding any explicit constructor prevents Groovy from auto-generating the
default one.

Co-Authored-By: Oz <oz-agent@warp.dev>
@jdaugherty
Copy link
Contributor

Closing this, i think teh core issue was the rest transforms weren't included in your project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants