@QueryProjection issues

439 views
Skip to first unread message

timowest

unread,
Sep 27, 2011, 7:23:39 AM9/27/11
to quer...@googlegroups.com
Hi. I'm having a problem using query projections. Basically the list(Expr) method of the query returns an ArrayList of Object[] containing constructor args of my DTO.

QueryDSL version in use 1.8.4.

This is my DTO:

public class UzemniCelek implements Serializable {
@QueryProjection
public UzemniCelek(Long key, String id, String popis) {
       
this.key = key;
       
this.id = id;
       
this.popis = popis;
}
...


This is the query construction:

JPAQuery q = new JPAQuery(em);
QObvodView ov = QObvodView.obvodView;
q
.from(ov);
...
QUzemniCelek uc = new QUzemniCelek( ov.krajKey, ov.krajId, ov.krajPopis);
Object o = q.list(uc);


Variable uc evaluates to: new UzemniCelek(obvodView.krajKey, obvodView.krajId, obvodView.krajPopis)

But the finally issued query looks like this:
Hibernate:
   /* select
        obvodView.krajKey,
        obvodView.krajId,
        obvodView.krajPopis
    from
        ObvodView obvodView
    order by
        obvodView.krajId asc */


The resulting list is not a List<UzemniCelek>, it's a list of arrays of objects containing values of the selected rows.

What am I doing wrong.
Thanks for your replay
JaPe

timowest

You are doing everything right. This is a bug in the JPAQuery's handling of constructor expressions. I am fixing it right now.

peremsky

This is a workaround I found:


...
EConstructor<UzemniCelek> uc = null;
uc
= QUzemniCelek
.create(UzemniCelek.class, ov.krajKey, ov.krajId, ov.krajPopis);
return q.list(uc);
...


JaPe

timowest

Fix commited to SVN trunk

Fixed in 1.9.0

mwalter

I thought this bug is fixed?

I'm using Querydsl 2.1.2 with OpenJPA 1.2.3 and I get a list of Object arrays returned using the static create() method as peremsky described.

I tried the workaround using class ConstructorExpression because EConstructor does not exist anymore. But the class ConstructorExpression does not accept my entity class as a parameter.

So what can I do now? Please help! Thank you!

Edit:
I tried the constructor projection with a plain POJO and it worked. But if the @QueryProjection annotated class is an JPA entity as well, it does not work! So please fix that! Same problem as peremsky had before.

timowest

Could you post the executed JPQL query?

> But the class ConstructorExpression does not accept my entity class as a parameter.

What do you mean by that?

I believe your problem is caused by the fact the Querydsl JPA does not yet support custom projections for other backends then Hibernate.

We have tested Querydsl JPA with Hibernate and EclipseLink, not OpenJPA.

Is there a specific API to declare custom projections / result transformation in OpenJPA? I believe this is not yet covered by the JPA 2 specs.

mwalter

First of all, we are using JPA 1.0

Here is the query Querydsl generates:
SELECT t0.VM_ART_ID, t0.VM_ART_ABK, t0.VM_ID, t0.VM_NR, t0.VM_LAUF_ID, t0.VM_LAUF_VARIANTE, t0.VP_NR, t0.BP_VON_ID, t0.BP_VON_BEZEICHNUNG, t0.BP_NACH_ID, t0.BP_NACH_BEZEICHNUNG, t0.VM_LAUF_ELEMENT_VON_AB_ZEIT, t0.VM_LAUF_ELEMENT_NACH_AN_ZEIT, t0.VM_STRECKE_ID, t0.VM_LINIE_ID, t0.VM_LINIE_NR, t0.TU_ID, t0.TU_CODE, t0.VM_LAUF_PLANUNGSTYP_CODE_ID, t0.VM_LAUF_OEFFENTLICH_TF, t0.VM_LAUF_GUELTIG_TF, t0.VM_LAUF_GELOESCHT_TF, t0.FPL_PERIODE_ID, t0.DATENQUELLE_ID
from vw_vm_suche t0
where
(t0.vm_nr = '1061')
GROUP BY t0
.VM_ART_ID, t0.VM_ART_ABK, t0.VM_ID, t0.VM_NR, t0.VM_LAUF_ID, t0.VM_LAUF_VARIANTE, t0.VP_NR, t0.BP_VON_ID, t0.BP_VON_BEZEICHNUNG, t0.BP_NACH_ID, t0.BP_NACH_BEZEICHNUNG, t0.VM_LAUF_ELEMENT_VON_AB_ZEIT, t0.VM_LAUF_ELEMENT_NACH_AN_ZEIT, t0.VM_STRECKE_ID, t0.VM_LINIE_ID, t0.VM_LINIE_NR, t0.TU_ID, t0.TU_CODE, t0.VM_LAUF_PLANUNGSTYP_CODE_ID, t0.VM_LAUF_OEFFENTLICH_TF, t0.VM_LAUF_GUELTIG_TF, t0.VM_LAUF_GELOESCHT_TF, t0.FPL_PERIODE_ID, t0.DATENQUELLE_ID


First thing, JPA constructor expressions use the following syntax:
SELECT NEW <fully qualified class name> ...
In the generated query above I do not see a NEW operator.

Second, the query should return a list of VwVmSucheDto objects:
return query.list(new QVwVmSucheDto(...))


Here is the generated constructor:

   
public QVwVmSucheDto(NumberExpression<Long> vmArtId, StringExpression vmArtAbk, NumberExpression<Long> vmId, StringExpression vmNr, NumberExpression<Long> vmLaufId, NumberExpression<Integer> vmLaufVariante, NumberExpression<Integer> vpNr, NumberExpression<Long> bpVonId, StringExpression bpVonBezeichnung, NumberExpression<Long> bpNachId, StringExpression bpNachBezeichnung, DateTimeExpression<? extends java.util.Calendar> vmLaufElementVonAbZeit, DateTimeExpression<? extends java.util.Calendar> vmLaufElementNachAnZeit, NumberExpression<Long> vmStreckeId, NumberExpression<Long> vmLinieId, StringExpression vmLinieNr, NumberExpression<Long> tuId, StringExpression tuCode, NumberExpression<Long> vmLaufPlanungstypCodeId, BooleanExpression vmLaufOeffentlich, BooleanExpression vmLaufGueltig, BooleanExpression vmLaufGeloescht, NumberExpression<Long> fplPeriodeId, NumberExpression<Long> datenquelleId) {
       
super(VwVmSucheDto.class, new Class[]{Long.class, String.class, Long.class, String.class, Long.class, Integer.class, Integer.class, Long.class, String.class, Long.class, String.class, java.util.Calendar.class, java.util.Calendar.class, Long.class, Long.class, String.class, Long.class, String.class, Long.class, Boolean.class, Boolean.class, Boolean.class, Long.class, Long.class}, vmArtId, vmArtAbk, vmId, vmNr, vmLaufId, vmLaufVariante, vpNr, bpVonId, bpVonBezeichnung, bpNachId, bpNachBezeichnung, vmLaufElementVonAbZeit, vmLaufElementNachAnZeit, vmStreckeId, vmLinieId, vmLinieNr, tuId, tuCode, vmLaufPlanungstypCodeId, vmLaufOeffentlich, vmLaufGueltig, vmLaufGeloescht, fplPeriodeId, datenquelleId);
   
}


If the query is executed I get an ArrayList which contains objects filled with the values instead of VwVmSucheDto. sad But this is quite clear because in the generated constructor new Class[]{...} is called which creates Object[]. Shouldn't it read new VwVmSucheDto here?

timowest

Could you try updating to SVN trunk?

I just fixed some issues with custom projections and non-Hibernate JPA providers. Here is a related ticket : https://bugs.launchpad.net/querydsl/+bug/542913

I am aware of the JPQL constructor expressions, but we needed a more general contract for FactoryExpressions.

mwalter

I checked out the latest sources and startet the build in querydsl-root with mvn install. This is the result:

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Querydsl .......................................... SUCCESS [13.641s]
[INFO] Querydsl - Core module ............................ SUCCESS [1:44.000s]
[INFO] Querydsl - APT support ............................ SUCCESS [2:21.000s]
[INFO] Querydsl - Collections support .................... SUCCESS [29.203s]
[INFO] Querydsl - SQL support ............................ FAILURE [6:04.406s]
[INFO] Querydsl - Maven plugin ........................... SKIPPED
[INFO] Querydsl - JPA support ............................ SKIPPED
[INFO] Querydsl - JDO support ............................ SKIPPED
[INFO] Querydsl - Lucene support ......................... SKIPPED
[INFO] Querydsl - Hibernate Search support ............... SKIPPED
[INFO] Querydsl - Mongodb support ........................ SKIPPED
[INFO] Querydsl - Scala support .......................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10:52.656s
[INFO] Finished at: Wed Jul 06 12:02:31 CEST 2011
[INFO] Final Memory: 46M/80M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project querydsl-sql: Could not resolve dependencies for project com.mysema.querydsl:querydsl-sql:jar:2.2.0.beta5-SNAPSHOT: Fa
iled to collect dependencies for [com.mysema.querydsl:querydsl-core:jar:2.2.0.beta5-SNAPSHOT (compile), org.slf4j:slf4j-api:jar:1.6.1 (compile), org.slf4j:slf4j
-log4j12:jar:1.6.1 (provided), joda-time:joda-time:jar:1.6 (compile), javax.validation:validation-api:jar:1.0.0.GA (compile), org.apache.ant:ant:jar:1.8.1 (prov
ided), hsqldb:hsqldb:jar:1.8.0.10 (test), org.apache.derby:derby:jar:10.8.1.2 (test), mysql:mysql-connector-java:jar:5.1.16 (test), postgresql:postgresql:jar:8.
4-701.jdbc4 (test), com.oracle:ojdbc5:jar:11.1.0.7.0 (test), net.sourceforge.jtds:jtds:jar:1.2.4 (test), com.h2database:h2:jar:1.3.157 (test), com.mysema.queryd
sl:querydsl-core:jar:tests:2.2.0.beta5-SNAPSHOT (test), commons-io:commons-io:jar:1.4 (test), jdepend:jdepend:jar:2.9.1 (test), junit:junit:jar:4.8.1 (test), or
g.easymock:easymock:jar:3.0 (test)]: Failed to read artifact descriptor for com.oracle:ojdbc5:jar:11.1.0.7.0: Could not transfer artifact com.oracle:ojdbc5:pom:
11.1.0.7.0 from/to jahia (http://maven.jahia.org/maven2): Error transferring file: Connection reset -> [Help 1]

Edit: Okay, the jahia server is down. angry

Can you provide the JAR somewhere?

timowest

I just uploaded the latest snapshot to our repo : http://source.mysema.com/maven2/snapshots/

mwalter

Okay, thank you very much! You are very fast! smile

Update:

I found out new interesting things:
The constructor used by Querydsl is very restrictive i.e. the constructor generated by Querydsl uses wrapper types only and always. But our DTO's constructor looks like that:

   
@QueryProjection
   
public VwVmSucheDto(final Long vmArtId, final String vmArtAbk, final Long vmId, final String vmNr, final Long vmLaufId, final int vmLaufVariante,
           
final int vpNr, final Long bpVonId, final String bpVonBezeichnung, final Long bpNachId, final String bpNachBezeichnung,
           
final Calendar vmLaufElementVonAbZeit, final Calendar vmLaufElementNachAnZeit, final Long vmStreckeId, final Long vmLinieId,
           
final String vmLinieNr, final Long tuId, final String tuCode, final Long vmLaufPlanungstypCodeId, final boolean vmLaufOeffentlich,
           
final boolean vmLaufGueltig, final boolean vmLaufGeloescht, final Long fplPeriodeId, final Long datenquelleId) {
...
}


As you can see we use some primitive values here. But Querydsl simply ignores that and generates this instead:


   
public QVwVmSucheDto(NumberExpression<Long> vmArtId, StringExpression vmArtAbk, NumberExpression<Long> vmId, StringExpression vmNr, NumberExpression<Long> vmLaufId, NumberExpression<Integer> vmLaufVariante, NumberExpression<Integer> vpNr, NumberExpression<Long> bpVonId, StringExpression bpVonBezeichnung, NumberExpression<Long> bpNachId, StringExpression bpNachBezeichnung, DateTimeExpression<? extends java.util.Calendar> vmLaufElementVonAbZeit, DateTimeExpression<? extends java.util.Calendar> vmLaufElementNachAnZeit, NumberExpression<Long> vmStreckeId, NumberExpression<Long> vmLinieId, StringExpression vmLinieNr, NumberExpression<Long> tuId, StringExpression tuCode, NumberExpression<Long> vmLaufPlanungstypCodeId, BooleanExpression vmLaufOeffentlich, BooleanExpression vmLaufGueltig, BooleanExpression vmLaufGeloescht, NumberExpression<Long> fplPeriodeId, NumberExpression<Long> datenquelleId) {
       
super(VwVmSucheDto.class, new Class[]{Long.class, String.class, Long.class, String.class, Long.class, Integer.class, Integer.class, Long.class, String.class, Long.class, String.class, java.util.Calendar.class, java.util.Calendar.class, Long.class, Long.class, String.class, Long.class, String.class, Long.class, Boolean.class, Boolean.class, Boolean.class, Long.class, Long.class}, vmArtId, vmArtAbk, vmId, vmNr, vmLaufId, vmLaufVariante, vpNr, bpVonId, bpVonBezeichnung, bpNachId, bpNachBezeichnung, vmLaufElementVonAbZeit, vmLaufElementNachAnZeit, vmStreckeId, vmLinieId, vmLinieNr, tuId, tuCode, vmLaufPlanungstypCodeId, vmLaufOeffentlich, vmLaufGueltig, vmLaufGeloescht, fplPeriodeId, datenquelleId);
   
}



As you can see in the line super(...) every value is a wrapper type, no primitives here.

Now if I execute a query using query projection I get java.lang.NoSuchMethodException: VwVmSucheDto.<init>(...) because the constructors do not match!

I got it to work with 2.2.0.beta5-SNAPSHOT by manually changing all the primitives to their wrapper types respectively. But this it not the way it was designed to, is it?

timowest

Could you file a bug on this?

> But this it not the way it was designed to, is it?

No ;)

mwalter

Okay, bug is reported: #806838

Is there a chance that you will fix it in the next release? smile

timowest

Yes, this will be probably fixed for the next release.
Reply all
Reply to author
Forward
0 new messages