How to use Enums in dynamically built queries

5,501 views
Skip to first unread message

Ati

unread,
Sep 24, 2014, 1:49:47 PM9/24/14
to quer...@googlegroups.com
Trying to build typed QueryDsl queries dynamically from strings.

I have my entity class Country:

@Entity
public class Country {

    private String name;
    private Continent continent;
}

where Continent is an Enumeration:

public enum Continent {

    Asia, Africa, North_America, South_America, Antarctica, Europe, Australia;

}

I build querydsl predicates this way:

PathBuilder<?> entityPath = new PathBuilder(domainClass, domainClass.getSimpleName().toLowerCase());
return Expressions.predicate(operator, entityPath.get(name), Expressions.constant(value));

My typical input looks like this: "continent eq Asia", which I want to translate to SQL query: SELECT * FROM COUNTRY WHERE CONTINENT = 'Asia';

So in the code snippet above I have domainclass = Country.class in this case and trying to figure out from "continent eq Asia" a querydsl query dynamically, so I got
 name = "continent" , Operation is EQ  and value = Continent.valueOf("Asia").

This works fine, but the problem comes out when I want to use a different operation, for example Ops.LIKE, Ops.STARTS_WITH, Ops.ENDS_WITH, etc.
So the input looks like this string: "continent sw A" (continent startsWith 'A') but the call of a valueOf method on my Continent enum ends with error, becasue I can't get an instance of the enum from a not full value.

Otherwise operations LIKE, STARTS_WITH and ENDS_WITH works fine with String, but I don't know how to change my code to be able to work with enum as well.
Thanks for your help.

Timo Westkämper

unread,
Sep 24, 2014, 2:39:23 PM9/24/14
to Querydsl on behalf of Ati
Hi.

Did you try using the strings directly instead of turning them into enums?

Timo

--
You received this message because you are subscribed to the Google Groups "Querydsl" group.
To unsubscribe from this group and stop receiving emails from it, send an email to querydsl+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ati

unread,
Sep 24, 2014, 4:18:43 PM9/24/14
to quer...@googlegroups.com
Yes, I placed string into value, but  I got an error. It expects an enum.

Ati

unread,
Sep 25, 2014, 2:21:23 AM9/25/14
to quer...@googlegroups.com
I always get this error when I put there a string.

java.lang.IllegalArgumentException: You have attempted to set a value of type class java.lang.String for parameter 1 with expected type of class com.mycompany.domain.Continent from query string select count(country)
from Country country
where country.continent <> ?1.
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:933)
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:907)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:469)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:1)
at com.mysema.query.jpa.impl.JPAUtil.setConstants(JPAUtil.java:55)
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:129)
at com.mysema.query.jpa.impl.AbstractJPAQuery.count(AbstractJPAQuery.java:84)
at com.mycompany.framework.common.repository.CommonRepositoryImpl.filter(CommonRepositoryImpl.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:442)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:427)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy70.filter(Unknown Source)

Timo Westkämper

unread,
Sep 25, 2014, 10:38:27 AM9/25/14
to Querydsl on behalf of Ati
Hi.

Could you also try enumExpression.stringValue() ?

Timo

--

Ati

unread,
Sep 26, 2014, 8:52:30 AM9/26/14
to quer...@googlegroups.com
Hi Timo,
Can you add more details please. At least how to build an enum expression?
Thanks

Ruben Dijkstra

unread,
Sep 26, 2014, 10:42:04 AM9/26/14
to quer...@googlegroups.com
you could register all your necessary paths (Country, Continent) in a map and when you get() it test what type it is
for instance:
Path<?> path = types.get(name);
if(path instanceof StringPath) {
   
StringPath<?> stringPath = (StringPath) path;
}else if(path instanceof EnumPath){
   
EnumPath<?> enumPath = (EnumPath<?>) path;
}
etc.

Or if you know the class at runtime, building an enum expression would be:
pathBuilder.getEnum("property", clazz.asSubclass(Enum.class));

I hope this helps you.

Ruben

Ati

unread,
Sep 26, 2014, 12:13:17 PM9/26/14
to quer...@googlegroups.com
Did you mean this way?

Expressions.predicate(operator,  entityPath.getEnum(name, propertyClass.asSubclass(Enum.class)), Expressions.constant(value));

Got this exception

java.lang.IllegalArgumentException: You have attempted to set a value of type class java.lang.String for parameter 1 with expected type of class Continent from query string select count(country)
from Country country
where country.continent like ?1 escape '!'.

Ruben Dijkstra

unread,
Sep 26, 2014, 12:25:06 PM9/26/14
to quer...@googlegroups.com
Hello,

almost, you forgot to use enumPath.stringValue()

also, you don't have to call Expressions.predicate, you can also use entityPath.getEnum(name, propertyClass.asSubclass(Enum.class)).stringValue() and build from there.

for you that would be entityPath.getEnum(name, propertyClass.asSubclass(Enum.class)).stringValue()[.eq(value), .startsWith(value), ...]
then you are sure you pass in the right parameters for the right hand side.

Ati

unread,
Sep 26, 2014, 1:34:30 PM9/26/14
to quer...@googlegroups.com
Hi Ruben,

Thanks for your help, that works fine for enum, but the problem is that I looking for a general solution for all datatypes, so ideally I would like to have just one call for all datatypes (String, Long, Date, Enum, etc.) and all operations, that is also the reason why I'm using Expressions.predicate () instead of somePath.[eq (value) or .startsWith(value)]

If you have any idea I will appreciate that
Ati

Timo Westkämper

unread,
Sep 26, 2014, 2:27:35 PM9/26/14
to Querydsl on behalf of Ati
Hi.

The stringValue conversion can also be applied to other literal types.

Timo

Ati

unread,
Sep 26, 2014, 2:42:38 PM9/26/14
to quer...@googlegroups.com
Yeah, but does it mean that I have to find out the exact type all the times?

Ati


On Friday, September 26, 2014 8:27:35 PM UTC+2, timowest wrote:
Hi.

The stringValue conversion can also be applied to other literal types.

Timo

Timo Westkämper

unread,
Sep 26, 2014, 2:45:23 PM9/26/14
to Querydsl on behalf of Ati
Hi.

stringValue uses Ops.STRING_CAST, you can use that also dynamically, no need to use EnumExpression, StringExpression etc explicitly.

For examples I prefer to use the typed expressions, since they are easier to use.

Br,
Timo


Ati

unread,
Sep 26, 2014, 3:16:34 PM9/26/14
to quer...@googlegroups.com
But what would you do when you would looking for an exactly one universal way to construct a predicate always from a string?

Thx
Ati


On Friday, September 26, 2014 8:45:23 PM UTC+2, timowest wrote:
Hi.

Timo Westkämper

unread,
Sep 26, 2014, 3:57:00 PM9/26/14
to Querydsl on behalf of Ati
Hi.

If you are searching for "path op constant" style pattern you should probably start by thinking of the possible types of path and constant. For EQ and NE as op you can convert the constant argument to the path type. For SARTS_WITH etc you have a String constant and will need to convert the path instead, via the STRING_CAST operation.

Does that help?

Timo
Reply all
Reply to author
Forward
0 new messages