The reference docs for Support for Vavr Collections only describe Vavr (io.vavr) collection/Option types as query method return types, and Hibernate's own parameter-binding code only checks value instanceof Collection (QueryParameterBindingImpl#handleAsMultiValue), with no Vavr-awareness — combined, it's reasonable to conclude Vavr types aren't supported as method parameters at all.
This isn't just a theoretical risk: reading those two facts together led to exactly this conclusion during an actual internal code review (unrelated to Spring Data), flagged as a correctness/runtime-crash issue — the claim was that a derived query method taking a Vavr collection as an IN-clause argument would throw IllegalArgumentException at runtime, silently breaking a bulk UPDATE in production and leaving stale rows behind.
That conclusion doesn't hold: Spring Data Commons unwraps Vavr collection/Option arguments into their Java-native equivalent before the value ever reaches Hibernate, on every method argument, not just return values:
ParametersParameterAccessor — constructor runs QueryExecutionConverters.unwrap(...) on every method argument whose type is a registered wrapper type.
QueryExecutionConverters — registers io.vavr.collection.Seq/Map/Set as wrapper types "as of 2.0" via CustomCollections.
CustomCollections — VavrToJavaCollectionConverter#convert calls ((Seq<?>) source).asJava() to hand back a real java.util.List/Map/Set.
JpaParametersParameterAccessor — extends ParametersParameterAccessor directly, so JPA inherits this unwrapping for free.
ParameterMetadataProvider — pulls bind values via accessor.iterator(), i.e., the already-unwrapped values, before Hibernate ever sees them.
This chain is unchanged across 2.7.0, 3.3.0, 3.5.13, and current main, and predates Vavr support itself — the constructor-level unwrap was added in commit 1185e71 (2015, DATACMNS-768) for java.util.Optional/Guava Optional; Vavr collections inherited it for free once registered in the same QueryExecutionConverters registry over a year later.
Minimal reproduction: on spring-data-jpa:3.5.13 / hibernate-core:6.6.53.Final / vavr:1.0.1 (or just Spring Boot 3.5.16 with Spring Data JPA and Vavr 1.0.1), a derived query method declared as findByStatusIn(io.vavr.collection.List<Status> activeStatuses) feeding an IN-clause bulk UPDATE was exercised end-to-end against a live database (verified twice, with fresh row-level before/after evidence each time) and filtered/updated rows correctly, with no exception.
The story below walks through how that played out end-to-end, and the lesson it left behind.
Story from that actual code review (click to expand)
During review of a derived query method along the lines of findByStatusIn(io.vavr.collection.List<Status> activeStatuses) feeding an IN-clause bulk UPDATE, a reviewer flagged it as a correctness/runtime-crash issue: io.vavr.collection.List isn't a java.util.Collection, Hibernate's JPQL IN-clause binder requires one, and — per this very doc page — Vavr's Spring Data integration "covers return-type wrapping only."
Conclusion: every call would throw IllegalArgumentException, the bulk update would silently never run, and stale rows would pile up in production.
That objection turned out to be wrong, but only after real work to prove it: verifying against the exact dependency versions in use (spring-data-jpa, hibernate-core, vavr) via live testing — hitting the running application twice and inspecting the database directly before and after each call, with fresh row-level evidence each time — plus re-tracing the exact source chain described above (ParametersParameterAccessor → QueryExecutionConverters → the Vavr-to-Java collection converter) to show why the observed behavior was correct, not a fluke.
The moral: none of that legwork should have been necessary.
The two facts driving the objection — this doc page, and Hibernate's instanceof Collection check — are both public and both correct in isolation; the doc gap is the only reason they add up to a wrong conclusion.
Documenting the parameter-side behavior turns a multi-hour/day, evidence-gathering review debate into a one-line link.
Could the "Support for Vavr Collections" section (source: query-methods-details.adoc) mention that these wrapper types are also accepted as query method parameters, not just return types?
That would save the next reader from having to trace through QueryExecutionConverters/ParametersParameterAccessor to confirm it, and prevent teams from concluding Vavr support is incomplete when it's actually just undocumented.
The same gap is duplicated in the Supported Query Return Types appendix (source: query-return-types-reference.adoc, same repo - verified byte-identical to the copy rendered under spring-data-jpa's reference docs).
Its row for Vavr collection types reads "Vavr collection types. See [Support for Vavr Collections] for details.", framing them as a return type only, same as the section above.
Could that row also note that these types are accepted as method parameters?
(Searched existing issues for "vavr" + "parameter"/"argument" — didn't find a prior report of this specific documentation gap.)
The reference docs for Support for Vavr Collections only describe Vavr (
io.vavr) collection/Optiontypes as query method return types, and Hibernate's own parameter-binding code only checksvalue instanceof Collection(QueryParameterBindingImpl#handleAsMultiValue), with no Vavr-awareness — combined, it's reasonable to conclude Vavr types aren't supported as method parameters at all.This isn't just a theoretical risk: reading those two facts together led to exactly this conclusion during an actual internal code review (unrelated to Spring Data), flagged as a correctness/runtime-crash issue — the claim was that a derived query method taking a Vavr collection as an
IN-clause argument would throwIllegalArgumentExceptionat runtime, silently breaking a bulkUPDATEin production and leaving stale rows behind.That conclusion doesn't hold: Spring Data Commons unwraps Vavr collection/
Optionarguments into their Java-native equivalent before the value ever reaches Hibernate, on every method argument, not just return values:ParametersParameterAccessor— constructor runsQueryExecutionConverters.unwrap(...)on every method argument whose type is a registered wrapper type.QueryExecutionConverters— registersio.vavr.collection.Seq/Map/Setas wrapper types "as of 2.0" viaCustomCollections.CustomCollections—VavrToJavaCollectionConverter#convertcalls((Seq<?>) source).asJava()to hand back a realjava.util.List/Map/Set.JpaParametersParameterAccessor— extendsParametersParameterAccessordirectly, so JPA inherits this unwrapping for free.ParameterMetadataProvider— pulls bind values viaaccessor.iterator(), i.e., the already-unwrapped values, before Hibernate ever sees them.This chain is unchanged across
2.7.0,3.3.0,3.5.13, and currentmain, and predates Vavr support itself — the constructor-level unwrap was added in commit1185e71(2015, DATACMNS-768) forjava.util.Optional/GuavaOptional; Vavr collections inherited it for free once registered in the sameQueryExecutionConvertersregistry over a year later.Minimal reproduction: on
spring-data-jpa:3.5.13/hibernate-core:6.6.53.Final/vavr:1.0.1(or just Spring Boot 3.5.16 with Spring Data JPA and Vavr 1.0.1), a derived query method declared asfindByStatusIn(io.vavr.collection.List<Status> activeStatuses)feeding anIN-clause bulkUPDATEwas exercised end-to-end against a live database (verified twice, with fresh row-level before/after evidence each time) and filtered/updated rows correctly, with no exception.The story below walks through how that played out end-to-end, and the lesson it left behind.
Story from that actual code review (click to expand)
During review of a derived query method along the lines of
findByStatusIn(io.vavr.collection.List<Status> activeStatuses)feeding anIN-clause bulkUPDATE, a reviewer flagged it as a correctness/runtime-crash issue:io.vavr.collection.Listisn't ajava.util.Collection, Hibernate's JPQLIN-clause binder requires one, and — per this very doc page — Vavr's Spring Data integration "covers return-type wrapping only."Conclusion: every call would throw
IllegalArgumentException, the bulk update would silently never run, and stale rows would pile up in production.That objection turned out to be wrong, but only after real work to prove it: verifying against the exact dependency versions in use (
spring-data-jpa,hibernate-core,vavr) via live testing — hitting the running application twice and inspecting the database directly before and after each call, with fresh row-level evidence each time — plus re-tracing the exact source chain described above (ParametersParameterAccessor→QueryExecutionConverters→ the Vavr-to-Java collection converter) to show why the observed behavior was correct, not a fluke.The moral: none of that legwork should have been necessary.
The two facts driving the objection — this doc page, and Hibernate's
instanceof Collectioncheck — are both public and both correct in isolation; the doc gap is the only reason they add up to a wrong conclusion.Documenting the parameter-side behavior turns a multi-hour/day, evidence-gathering review debate into a one-line link.
Could the "Support for Vavr Collections" section (source:
query-methods-details.adoc) mention that these wrapper types are also accepted as query method parameters, not just return types?That would save the next reader from having to trace through
QueryExecutionConverters/ParametersParameterAccessorto confirm it, and prevent teams from concluding Vavr support is incomplete when it's actually just undocumented.The same gap is duplicated in the Supported Query Return Types appendix (source:
query-return-types-reference.adoc, same repo - verified byte-identical to the copy rendered underspring-data-jpa's reference docs).Its row for Vavr collection types reads "Vavr collection types. See [Support for Vavr Collections] for details.", framing them as a return type only, same as the section above.
Could that row also note that these types are accepted as method parameters?
(Searched existing issues for "vavr" + "parameter"/"argument" — didn't find a prior report of this specific documentation gap.)