[kotlin-spring] Move cascading @Valid off containers and kotlin.Any properties to element types (HV000271/HV000272)

IN
17h ago
+54-46

Description

kotlin-spring with useBeanValidation (the default) puts @field:Valid on container properties (List, Set, Map) and on free-form kotlin.Any properties. Hibernate Validator 9.1 (shipped with Spring Boot 4.1) deprecated container-level @Valid and logs HV000271/HV000272 while building bean metadata. Because the bean metadata cache is backed by soft references, the warnings are not only logged at startup: they come back every time the metadata of a rarely used model is evicted and rebuilt, so production logs end up with a steady stream of them.

This PR applies to the Kotlin templates what the Java generators got in #24176 (issue #23614, released in 7.26.0):

  • beanValidation.mustache no longer emits @field:Valid for containers. Cascading goes through the element type instead (List<@Valid T>, Set<@Valid T>), which the element annotation infrastructure added in #25592 already renders for arrays/sets; the deprecated duplicate on the container itself is gone.
  • Map values now cascade through the type argument as well: Map<String, @Valid V> (the generator opts into useBeanValidationOnMapValueType()). This deliberately preserves cascading validation of map values, which the earlier attempt #24027/#24029 dropped along with the annotation, and which was the reason that PR was closed.
  • Free-form type: object and {} properties (rendered as kotlin.Any) no longer get @field:Valid - there is nothing to cascade into (HV000272).
  • Bean-typed properties keep @field:Valid, so cascading into a single $ref property is unchanged.

Since the Kotlin compiler only writes type-use annotations into bytecode when compiled with -Xemit-jvm-type-annotations, note that the generated Gradle and Maven builds already set this flag when useBeanValidation is enabled (added in #25592). If you compile the generated sources with your own build, keep setting that flag - without it, cascading into elements silently stops (same situation as the array/set element annotations introduced in #25592).

Generated output change

// before
data class Pet(
    @field:Valid
    @get:JsonProperty("items") val items: kotlin.collections.List<Item>? = null,

    @field:Valid
    @get:JsonProperty("customInfo") val customInfo: kotlin.Any? = null
)

// after
data class Pet(
    @get:JsonProperty("items") val items: kotlin.collections.List<@Valid Item>? = null,

    @get:JsonProperty("customInfo") val customInfo: kotlin.Any? = null
)

which matches the expected output from the issue.

Testing

  • KotlinSpringServerCodegenTest: updated the collection element annotation tests for the new map value rendering, and added fieldValidOnlyOnBeanTypedProperties, which asserts @field:Valid is present on a $ref-typed property and absent on free-form object, {} and free-form map properties.
  • mvn -pl modules/openapi-generator test: green (5756 tests, 0 failures).
  • Regenerated all kotlin-spring samples with bin/generate-samples.sh bin/configs/kotlin-spring-*.yaml: 35 deprecated @field:Valid annotations removed across 34 model files, no other diff.
  • Compiled the regenerated kotlin-springboot sample with Gradle and checked the compiled model with javap: the field carries javax.validation.Valid in RuntimeVisibleTypeAnnotations at TYPE_ARGUMENT(0), which is exactly what Hibernate Validator 9.x reads for cascading into elements, and which no longer logs HV000271.

PR checklist

  • Read the contribution guidelines.
  • Ran the sample generation scripts under ./bin/ to update the kotlin-spring Petstore samples so that CIs can verify the change.
  • Filed the PR against the correct branch: master.

Fixes #25347


Summary by cubic

Fixes #25347. kotlin-spring no longer emits the deprecated container-level @field:Valid on List/Set/Map properties, which Hibernate Validator 9.1+ (Spring Boot 4.1) warns about with HV000271/HV000272. Cascading is preserved through element-type annotations instead (List<@Valid T>, Map<String, @Valid V>), and bean-typed properties keep @field:Valid.

Free-form object/{} properties rendered as kotlin.Any lose the annotation since there is nothing to cascade into. Map value cascading is preserved via the type argument, unlike the closed attempt #24029 which dropped it entirely.

Type-use annotations are only visible to Hibernate Validator when compiled with -Xemit-jvm-type-annotations; the generated Gradle/Maven builds already set this flag when useBeanValidation is enabled.