Add TypeWrapperFactoryExpression for Type-Safe Custom Number Mapping in Querydsl Aggregations#1181
Merged
velo merged 2 commits intoOpenFeign:masterfrom Jun 9, 2025
Conversation
- Implement TypeWrapper<S, T> as a FactoryExpression - Allows converting a source Expression (e.g. BigDecimal) to a domain type (e.g. Money) - Prepares QueryDSL core to support custom aggregation projections without core API changes
- Verify IllegalArgumentException for unsupported custom types without wrapper - Verify sum-and-wrap to Money via TypeWrapper in both direct and DTO projection use cases - Ensure both positive and negative paths are exercised
Codecov ReportAll modified and coverable lines are covered by tests ✅
✅ All tests successful. No failed tests found. Additional details and impacted files@@ Coverage Diff @@
## master #1181 +/- ##
============================================
+ Coverage 61.19% 71.04% +9.85%
Complexity 84 84
============================================
Files 831 858 +27
Lines 32015 32359 +344
Branches 3590 3600 +10
============================================
+ Hits 19590 22988 +3398
+ Misses 11179 8148 -3031
+ Partials 1246 1223 -23 ☔ View full report in Codecov by Sentry. |
velo
approved these changes
Jun 9, 2025
velo
pushed a commit
that referenced
this pull request
Jun 9, 2025
Add `TypeWrapperFactoryExpression` for Type-Safe Custom Number Mapping in Querydsl Aggregations (#1181) * Introduce TypeWrapper to wrap Expression results into custom types - Implement TypeWrapper<S, T> as a FactoryExpression - Allows converting a source Expression (e.g. BigDecimal) to a domain type (e.g. Money) - Prepares QueryDSL core to support custom aggregation projections without core API changes * Add JPAQueryCustomTypeWrapperTest covering success and failure scenarios - Verify IllegalArgumentException for unsupported custom types without wrapper - Verify sum-and-wrap to Money via TypeWrapper in both direct and DTO projection use cases - Ensure both positive and negative paths are exercised Co-authored-by: 차동민 <40655807+chadongmin@users.noreply.github.com>
chadongmin
added a commit
to chadongmin/querydsl
that referenced
this pull request
Jun 9, 2025
…g in Querydsl Aggregations (OpenFeign#1181) * Introduce TypeWrapper to wrap Expression results into custom types - Implement TypeWrapper<S, T> as a FactoryExpression - Allows converting a source Expression (e.g. BigDecimal) to a domain type (e.g. Money) - Prepares QueryDSL core to support custom aggregation projections without core API changes * Add JPAQueryCustomTypeWrapperTest covering success and failure scenarios - Verify IllegalArgumentException for unsupported custom types without wrapper - Verify sum-and-wrap to Money via TypeWrapper in both direct and DTO projection use cases - Ensure both positive and negative paths are exercised
velo
added a commit
that referenced
this pull request
Jun 9, 2025
* Add `TypeWrapperFactoryExpression` for Type-Safe Custom Number Mapping in Querydsl Aggregations (#1181) * Introduce TypeWrapper to wrap Expression results into custom types - Implement TypeWrapper<S, T> as a FactoryExpression - Allows converting a source Expression (e.g. BigDecimal) to a domain type (e.g. Money) - Prepares QueryDSL core to support custom aggregation projections without core API changes * Add JPAQueryCustomTypeWrapperTest covering success and failure scenarios - Verify IllegalArgumentException for unsupported custom types without wrapper - Verify sum-and-wrap to Money via TypeWrapper in both direct and DTO projection use cases - Ensure both positive and negative paths are exercised * Fix: Improve handling of `contains()` on JPA collections mapped with `@Converter` to basic types (#1199) * Fix: Early error for .contains() on @converter mapped JPA collections * Test : add test code * Add test cases to improve test coverage * style: revert formatting change based on review feedback for consistency * fix: correct escape & anchor handling in likeToRegex/regexToLike (#1174) * fix: correct escape & anchor handling in likeToRegex/regexToLike - treat \% \_ as literals, escape regex metachars, strip outer ^/$ only - add unit tests to guarantee accurate LIKE ↔ regex round-trips * Fix code format Signed-off-by: Marvin Froeder <velo.br@gmail.com> --------- Signed-off-by: Marvin Froeder <velo.br@gmail.com> Co-authored-by: Marvin Froeder <velo.br@gmail.com> --------- Signed-off-by: Marvin Froeder <velo.br@gmail.com> Co-authored-by: 차동민 <40655807+chadongmin@users.noreply.github.com> Co-authored-by: renechoi <115696395+renechoi@users.noreply.github.com> Co-authored-by: Marvin Froeder <velo.br@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background & Problem
Querydsl’s aggregation functions (e.g.,
sum(),sumAggregate()) return raw numeric types likeBigDecimalorDouble. However, when attempting to map these directly to customNumbersubtypes (e.g.,Money,MyCustomNumber), Querydsl throws:This limitation forces developers to write manual conversion logic, increasing boilerplate and risking errors in complex queries. The issue was originally reported in Issue #3550 and persists in the latest Querydsl release (6.11). A reproducible example is provided here:
https://github.com/chadongmin/querydsl-jpa-customnumber-aggregate-conversion-bug
Discussion
The problem stems from Querydsl’s internal casting mechanism (
MathUtils#cast), which lacks support for customNumbersubclasses. Community feedback highlighted two viable strategies:Extend
MathUtils#castModify the core
castmethod to automatically handle customNumbersubclasses.Introduce
TypeWrapperAbstractionCreate a
TypeWrappermechanism to carry target-type metadata for specific expressions, enabling type-safe mapping only when explicitly applied.Deliberation
Extending MathUtils#cast would have required invasive changes to a core utility used by every numeric operation, risking regressions and complicating future QueryDSL upgrades. By contrast, TypeWrapper lets me opt in only where needed, avoids touching existing casting logic, and cleanly isolates the custom-type support in one place—greatly simplifying maintenance and preserving backward compatibility.
Chosen Solution
I’ve implemented the TypeWrapper extension for FactoryExpression contexts. Now, any expression—be it sum(), avg(), or a factory-based projection—wrapped with TypeWrapper.wrap(expr, MyCustomNumber.class) is correctly serialized and mapped back to the custom type, while all unwrapped code paths remain unchanged.
TypeWrapper<S,T>to querydsl-core as aFactoryExpressionimplementation.JPAQueryCustomTypeWrapperTestin querydsl-jpa covering:@QueryProjectionDTO.Next Steps
If there’s a better approach or any improvements you’d suggest, please let me know—I’ll be happy to incorporate your feedback into this PR.