Skip to content
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

ArrayIndexOutOfBoundsException when using FactoryExpressionBase.skipNulls() #1496

Closed
pvcastro opened this issue Aug 24, 2015 · 18 comments
Closed
Labels
bug
Milestone

Comments

@pvcastro
Copy link

@pvcastro pvcastro commented Aug 24, 2015

I have the following code:

public class Credenciamento {

@QueryProjection
    public Credenciamento(Long id, LocalDate dataCriacao, LocalDate dataFim, InstituicaoCredora instituicaoCredora, Binario binario) {
        this.id = id;
        this.dataCriacao = dataCriacao;
        this.dataFim = dataFim;
        this.instituicaoCredora = instituicaoCredora;
        this.binario = binario;
    }

}

public Credenciamento consultar(Credenciamento credenciamentoPesquisa) {
        return newQuery()//
                .select(QCredenciamento.create(credenciamento.id, credenciamento.dataCriacao, credenciamento.dataFim, //
                        instituicaoCredora, //
                        QBinario.create(binario.id, binario.contentType, //
                                QArmazemDeBinario.create(armazemDeBinario.id, armazemDeBinario.caminho).skipNulls())//
                                .skipNulls()))//
                .from(credenciamento)//
                .join(credenciamento.instituicaoCredora, instituicaoCredora)//
                .leftJoin(credenciamento.binario, binario)//
                .leftJoin(binario.armazem, armazemDeBinario)//
                .where(credenciamento.id.eq(credenciamentoPesquisa.getId())).fetchOne();
    }

I'm trying to use the skipNulls method from the FactoryExpression class because I'm always getting empty classes when using a projected query and when leftJoin doesn't have an association in the database.

In the example above, a "Credenciamento" search performs a leftJoin with "Binario", but when there isn't any associated Binario in the database, instead of getting a null Binario passed to Credenciamento, I'm getting an instantiated Binario with empty attributes (the associated ArmazemDeBinario inside Binario is also empty instead of null).

I'm trying to use skipNulls like in my consultar method above, and I'm getting an ArrayIndexOutOfBoundsException in the following method from ArrayUtils.

I'm using QueryDSL 4.0.1.

// copied and modified from commons-lang-2.3
    // originally licensed under ASL 2.0
    public static Object[] subarray(Object[] array, int startIndexInclusive, int endIndexExclusive) {
        int newSize = endIndexExclusive - startIndexInclusive;
        Class<?> type = array.getClass().getComponentType();
        if (newSize <= 0) {
            return (Object[]) Array.newInstance(type, 0);
        }
        Object[] subarray = (Object[]) Array.newInstance(type, newSize);
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

Stacktrace:

java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at com.querydsl.core.util.ArrayUtils.subarray(ArrayUtils.java:53)
at com.querydsl.core.types.FactoryExpressionUtils.compress(FactoryExpressionUtils.java:154)
at com.querydsl.core.types.FactoryExpressionUtils.access$100(FactoryExpressionUtils.java:28)
at com.querydsl.core.types.FactoryExpressionUtils$FactoryExpressionAdapter.newInstance(FactoryExpressionUtils.java:62)
at com.querydsl.jpa.FactoryExpressionTransformer.transformTuple(FactoryExpressionTransformer.java:51)
at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:95)
at org.hibernate.loader.hql.QueryLoader.getResultList(QueryLoader.java:465)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2370)
at org.hibernate.loader.Loader.list(Loader.java:2365)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:497)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:387)
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:236)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1264)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:103)
at org.hibernate.internal.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:966)
at com.querydsl.jpa.hibernate.AbstractHibernateQuery.fetchOne(AbstractHibernateQuery.java:330)
at br.com.dataeasy.chronus.persistencia.hibernate.dao.CredenciamentoDAO.consultar(CredenciamentoDAO.java:88)
at br.com.dataeasy.chronus.service.CredenciamentoService.consultar(CredenciamentoService.java:59)

@Shredder121
Copy link
Member

@Shredder121 Shredder121 commented Aug 24, 2015

Could you maybe use code fences?

```java
// code here
```
@pvcastro
Copy link
Author

@pvcastro pvcastro commented Aug 24, 2015

Is this ok?

@Shredder121
Copy link
Member

@Shredder121 Shredder121 commented Aug 24, 2015

Yes thank you, this is much better than manually counting parentheses.

@pedro-borges
Copy link

@pedro-borges pedro-borges commented Aug 24, 2015

Just for the record, I have experienced the same issue with the following code:

JPAQuery(getEM()).from(foxtrotInvoice)
                .join(foxtrotInvoice.externalCompany, foxtrotExternalCompany)
                .join(foxtrotExternalCompany.company, foxtrotCompany)
                .where(foxtrotCompany.id.in(companyIds)
                        .and(foxtrotInvoice.financingState.eq(FinancingState.APPROVED))
                        .or(foxtrotInvoice.financingState.eq(FinancingState.FINANCED)))
                .groupBy(foxtrotCompany.id, foxtrotCompany.creditLimit, foxtrotCompany.delphi, foxtrotCompany.experianDbt)
                .map(foxtrotCompany.id,
                        QBook.create(
Book.class,foxtrotInvoice.financingAmount.sum().coalesce(BigDecimal.ZERO),
foxtrotInvoice.financingAmount.multiply(foxtrotCompany.delphi).sum().coalesce(BigDecimal.ZERO),
foxtrotInvoice.financingAmount.multiply(foxtrotInvoice.financingInterestRate).sum().coalesce(BigDecimal.ZERO)
                        ));
@pedro-borges
Copy link

@pedro-borges pedro-borges commented Aug 24, 2015

There is something fishy going on in FactoryExpressionTransformer.transformTuple(Object[], String[])
It starts with a tuple of Long, BigDecimal, BigDecimal, BigDecimal and instead of turning it into a Tuple of Long,Book(BigDecimal, BigDecimal, BigDecimal) it is turning it into a tuple of Long,BigDecimal simply truncating the last two fields. I am running version 3.6.6, but the same behaviour happens with 4.0.3

@timowest
Copy link
Member

@timowest timowest commented Aug 24, 2015

What happens here: com.querydsl.jpa.FactoryExpressionTransformer.transformTuple(FactoryExpressionTransformer.java:51)? How big is the tuple array?

@pedro-borges
Copy link

@pedro-borges pedro-borges commented Aug 24, 2015

The incoming Object[] = {Long, BigDecimal}, so it's size 2.
The incoming alias[] = {"0", "1", "2", "3"}
Notice I'm trying to return a Map<Long, Book> where Book is created from 3 BigDecimals via QBook.create().

@timowest
Copy link
Member

@timowest timowest commented Aug 24, 2015

Thanks. And what about @pvcastro?

@pvcastro
Copy link
Author

@pvcastro pvcastro commented Aug 25, 2015

My tuple array at this point is size 5, but the last element is null.

@pedro-borges
Copy link

@pedro-borges pedro-borges commented Aug 25, 2015

Please consider the edit in my reply above. I was answering from memory and just now reverted the code to properly debug it.

@timowest
Copy link
Member

@timowest timowest commented Aug 27, 2015

I did not manage to replicate the issue in the PR. Could you take a look? Or maybe provide a minimal example that has the same error?

@pvcastro
Copy link
Author

@pvcastro pvcastro commented Aug 27, 2015

Hi @timowest , are you asking me or @pedro-borges ?

@timowest
Copy link
Member

@timowest timowest commented Aug 27, 2015

Both

@pvcastro
Copy link
Author

@pvcastro pvcastro commented Aug 27, 2015

You could try a projected query left-joining another entity, with no result found for the association:

public class Entity1 {

    @ManyToOne
    private Entity2 entity2;

    @QueryProjection
    public Entity (Long id, Entity2 entity2) {
        this.id = id;
        this.entity2 = entity2;
    }

}

public class Entity2 {

    @QueryProjection
    public Entity2 (Long id) {
        this.id = id;
    }

}

public Entity1 find() {
        QEntity1 entity1 = QEntity1.entity1;
        QEntity2 entity2 = QEntity2.entity2;
        return new HibernateQuery<Entity1>(getSession())//
                .select(QEntity1.create(entity1.id, QEntity2.create(entity2.id).skipNulls()))//
                .from(entity1)//
                .leftJoin(entity1.entity2, entity2)//
                .fetchOne();
}

Try the code above, with a row contaning a null value for entity2 in the Entity1 table.
With skipNulls I get the ArrayIndexOutOfBoundsException, and without I get an empty Entity2.

@pedro-borges
Copy link

@pedro-borges pedro-borges commented Aug 31, 2015

Sorry for not replying on time, I believe the issue has been isolated and solved by now.
Will there be an update on this page stating the release this changes will come out in?

Thanks, keep up the great work

@johnktims
Copy link
Member

@johnktims johnktims commented Aug 31, 2015

This fix should be part of the release that's being made today.

@timowest timowest added this to the 4.0.4 milestone Aug 31, 2015
@timowest timowest removed the progress label Aug 31, 2015
@pvcastro
Copy link
Author

@pvcastro pvcastro commented Sep 4, 2015

Great job! Thanks for the fix!
I have a question though. Shouldn't this "skipNulls" be the default behavior? If you don't find a particular joined association, shouldn't it be null without having to explicitly specify it?

@timowest
Copy link
Member

@timowest timowest commented Sep 7, 2015

For backwards compatibility skipNulls is not the default. Also with multiple independent bindings to a projection, skipNulls might not always be the expected behaviour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.