New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JPA + custom type + Number + NumberExpression throws IllegalArgument Unsupported target type #261
Comments
Digging around the code: Conversions.java around line 64: private static boolean needsNumberConversion(Expression<?> expr) {
expr = ExpressionUtils.extract(expr);
return Number.class.isAssignableFrom(expr.getType()) && !Path.class.isInstance(expr);
} If a value needs to be treated as if Possible fix: I cannot try this out since I cannot get QueryDSL to compile on my machine, any hints appreciated :) |
I would like to ask if they have not been published to the maven library and can only be introduced into the project in the form of manual jars. |
I think this is an issue with the way hibernate 6 is doing the type mappings. It used to be that The custom implementation may need some extra work to get it to work properly. |
@HonoluluHenk why is it not compiling for you? Did you add the
Also I build using java 21. |
Thanks for the hint with toolchain.xml, I can build now! After you comment on hibernate 6 not returning the specific datatype anymore, I digged into to JPA spec and found: See JPA 3.1 chapter 4.8.5 Aggregate Functions in the SELECT Clause and also JPA 2.2, same chapter:
|
Hence the changes with sum(). I am not really happy with the solution though. I kept thinking about it and I think it would have been better to have some subclasses of |
For those arriving via google and being in a hurry: here's a workaround: // create a custom, re-usable QueryDSL wrapper:
static class TypeWrapper<DBType, MyType> implements com.querydsl.core.types.FactoryExpression<MyType> {
private final Class<MyType> valueClass;
private final Function<DBType, MyType> factory;
private final List<Expression<?>> args;
public TypeWrapper(Expression<DBType> arg, Class<MyType> valueClass, Function<DBType, MyType> factory) {
this.valueClass = valueClass;
this.factory = factory;
this.args = List.of(arg);
}
@Override
public <R, C> @Nullable R accept(Visitor<R, C> v, @Nullable C context) {
return v.visit(this, context);
}
@Override
public Class<? extends MyType> getType() {
return valueClass;
}
@Override
public List<Expression<?>> getArgs() {
return args;
}
@Override
@Nullable
public MyType newInstance(Object... args) {
@SuppressWarnings("unchecked")
DBType arg = (DBType) args[0];
return factory.apply(arg);
}
}
// ... and use it like this:
@Test
void query_with_NumberExpression_and_aggregate() {
MyCustomNumber actual = new JPAQuery<>(em)
.select(new TypeWrapper<>(
myEntity.myCustomNumber.sumBigDecimal(),
MyCustomNumber.class,
MyCustomNumber::new
))
.from(myEntity)
.fetchOne();
// instead throws:
// java.lang.IllegalArgumentException: Unsupported target type : MyCustomNumber
assertThat(actual)
.isInstanceOf(MyCustomNumber.class)
.isEqualTo(MY_CUSTOM_NUMBER);
} |
Just got an idea: why not add this function to NumberExpression - or any Expression for that matter? public <MyType> TypeWrapper<T, MyType> wrapValue(Class<MyType> myTypeClass, Function<T, MyType> factory) {
return new TypeWrapper<>(this, myTypeClass, factory);
} Not the most elegant but this could be a nice little typesafe but convent way for all Projections on value types. |
Hello! I did some digging for this issue. Wrote a relevant test, where I added a type
The problem is, what would be the best way to approach it? I can think of a few options: Have QueryDSL understand the JPA converters infrastructureIn this case QueryDSL would apply One alternative would be to limit ourselves to reading any Manually provide QueryDSL with information about which converter to use per caseIn this case we may want to tweak the There may be a way to enforce compile-time safety, but I am not experience enough in the inner workings of QueryDSL to even think of a way. If people think this is the best way to go, I would investigate. This solution looks a lot like the Allow the developer to override/extend the mapping logic of QueryDSLAt least for the case of a type that extends A much more generic mechanism of mapping types; or extending the QueryDSL metamodelI imagine that the same could happen if I want to persist e.g. an Maybe it would be worth investigating if the QueryDSL metamodel could be made plugable. What else???Ideas??? This is just a bunch of ideas, which may be irrelevant in the end; or may be good but not worth the effort in the end; or the outcome of this conversation is that it is a bad practice to let the persistence model know of custom value classes(*). I am investigating QueryDSL for my current project and I am happy to have found that there is an effort to keep it alive. Thank you @velo and team, I hope I will be able to contribute my own penny. (*) Until now I have applied this rule of thumb for my entities: use a set of rich value classes in the model ( |
Observed vs. expected behavior
(This is a continuation from the original QueryDSL issue #3550)
When using the following Combination
@Converter
/AttributeConverter
(also happens with all other Hibernate custom types like UserType)sumAggregate()
(formerly: sum())Example:
Actual outcome:
java.lang.IllegalArgumentException: Unsupported target type : MyCustomNumber
is thrown instead of a valid result.Expected outcome:
The query-result.
Steps to reproduce
I created a reproducer with unit tests showing what works and what doesn't:
https://github.com/dvbern/bug-reproducers-querydsl-jpa-hibernate6/tree/openfeign
Environment
Querydsl version: 6.0.0.RC1
Querydsl module: querydsl-jpa
Database: any (e.g. hsqldb, postgresql)
JDK: 17 (but the stacktrace indicates this is not a JDK-problem)
The text was updated successfully, but these errors were encountered: