java.lang.ClassCastException: [B cannot be cast to [Ljava.lang.Object; while using JPA 2.2 query.getResultStream().findFirst()

huangapple go评论70阅读模式
英文:

java.lang.ClassCastException: [B cannot be cast to [Ljava.lang.Object; while using JPA 2.2 query.getResultStream().findFirst()

问题

以下是翻译好的内容:

我的Spring Data存储库方法的代码如下所示:

public Optional<byte[]> findShipmentLabelByClientIdAndAwb(String clientId, String awb) {

    String queryString = "select g.shipmentLabel as shipmentLabel from GenericShipment g where g.client.id = :clientId and g.shipmentId = :awb " +
        " AND (g.processingStatus is null or g.processingStatus <> 'DELETED') AND g.shipmentLabel is not null";

    val query = entityManager.createQuery(queryString, byte[].class);

    query.setParameter("clientId", clientId);
    query.setParameter("awb", awb);

    return query.getResultStream().findFirst();
}

正如您所见,我正在尝试获取名为shipmentLabel的列,该列在我的Postgres模式中定义为bytea类型。
在运行时发生了以下异常:

java.lang.ClassCastException: 无法将[B强制转换为[Ljava.lang.Object;
at org.hibernate.internal.ScrollableResultsImpl.prepareCurrentRow(ScrollableResultsImpl.java:203)
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:101)
at org.hibernate.query.internal.ScrollableResultsIterator.hasNext(ScrollableResultsIterator.java:33)
at java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1811)
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:499)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:486)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:531)
at org.hibernate.query.spi.StreamDecorator.findFirst(StreamDecorator.java:260)

我想知道这是否是预期的行为,谢谢您提前的回答。

目前的解决方法是使用JPA 2.1变体:

return query.getResultList().stream().findFirst();

我使用的环境是Spring Boot 2.3.3Hibernate版本为5.4.20

英文:

The code of my Spring Data repository method is as follows:

public Optional&lt;byte[]&gt; findShipmentLabelByClientIdAndAwb(String clientId, String awb) {

    String queryString = &quot;select g.shipmentLabel as shipmentLabel from GenericShipment g where g.client.id = :clientId and g.shipmentId = :awb &quot; +
        &quot; AND (g.processingStatus is null or g.processingStatus &lt;&gt; &#39;DELETED&#39;) AND g.shipmentLabel is not null&quot;;

    val query = entityManager.createQuery(queryString, byte[].class);

    query.setParameter(&quot;clientId&quot;, clientId);
    query.setParameter(&quot;awb&quot;, awb);

    return query.getResultStream().findFirst();

}

As you can see, I am attempting to fetch, as byte array, the shipmentLabel column, defined in my Postgres schema as bytea.
The following exception occurs at runtime:

> java.lang.ClassCastException: [B cannot be cast to [Ljava.lang.Object;
> at org.hibernate.internal.ScrollableResultsImpl.prepareCurrentRow(ScrollableResultsImpl.java:203)
> at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:101)
> at org.hibernate.query.internal.ScrollableResultsIterator.hasNext(ScrollableResultsIterator.java:33)
> at java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1811)
> at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
> at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:499)
> at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:486)
> at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
> at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
> at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
> at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:531)
> at org.hibernate.query.spi.StreamDecorator.findFirst(StreamDecorator.java:260)

I was wondering if this is the intended behaviour here or not, thanks in advance for your answers.

For the time being, the workaround is to use the JPA 2.1 variant:

> return query.getResultList().stream().findFirst();

As environment, I am using Spring Boot 2.3.3, Hibernate version is 5.4.20.

答案1

得分: 3

尝试首先使用getResultList,看看是否有效:

public Optional<Byte[]> findShipmentLabelByClientIdAndAwb(
        String clientId, String awb) {
    return entityManager.createQuery("""
        select 
            g.shipmentLabel as shipmentLabel 
        from GenericShipment g 
        where 
            g.client.id = :clientId and 
            g.shipmentId = :awb and 
            (
                g.processingStatus is null or 
                g.processingStatus <> 'DELETED'
            ) and 
            g.shipmentLabel is not null
        """)
    .setParameter("clientId", clientId)
    .setParameter("awb", awb)
    .setMaxResults(1)
    .getResultList()
    .stream()
    .findFirst();
}

注意,仅仅为了使用findFirst从中选择第一个记录而选择N条记录是低效的做法。如果此查询返回100条记录,那么仍将从数据库中选择全部100条记录。

这就是为什么我添加了setMaxResults调用。

如果这不起作用,请尝试调试Hibernate的BinaryType,看看为什么它不返回byte[]

英文:

Try with getResultList first, and see if it works:

public Optional&lt;Byte[]&gt; findShipmentLabelByClientIdAndAwb(
		String clientId, String awb) {
    return entityManager.createQuery(&quot;&quot;&quot;
		select 
			g.shipmentLabel as shipmentLabel 
		from GenericShipment g 
		where 
			g.client.id = :clientId and 
			g.shipmentId = :awb and 
			(
				g.processingStatus is null or 
				g.processingStatus &lt;&gt; &#39;DELETED&#39;
			) and 
			g.shipmentLabel is not null
	    &quot;&quot;&quot;)
	.setParameter(&quot;clientId&quot;, clientId)
	.setParameter(&quot;awb&quot;, awb)
	.setMaxResults(1)
	.getResultList()
	.stream()
	.findFirst();
}

Note that it's inefficient to select N records only to take the first one using fidFirst. What if this query returns 100 records? You'd still select all 100 from the DB.

That's why I added the setMaxResults call.

If that doesn't work, try debugging the Hibernate BinaryType and see why it doesn't return byte[].

答案2

得分: 0

没有字节的原始流您可以在调试器中检查获取的流的类型可能是`Stream<Byte[]>``Stream<Object[]>`。这可以解释您遇到的异常使用Byte[]应该解决您的问题

public Optional<Byte[]> findShipmentLabelByClientIdAndAwb(String clientId, String awb) {
    String queryString = "select g.shipmentLabel as shipmentLabel from GenericShipment g where g.client.id = :clientId and g.shipmentId = :awb " +
        " AND (g.processingStatus is null or g.processingStatus <> 'DELETED') AND g.shipmentLabel is not null";

    val query = entityManager.createQuery(queryString, Byte[].class);

    query.setParameter("clientId", clientId);
    query.setParameter("awb", awb);

    return query.getResultStream().findFirst();
}
英文:

There is no primitive stream for byte, you can check in the debugger what type of stream your are getting maybe Stream&lt;Byte[]&gt; or Stream&lt;Object[]&gt;. This can explain the exception you are getting. Using Byte[] should solve your issue.

public Optional&lt;Byte[]&gt; findShipmentLabelByClientIdAndAwb(String clientId, String awb) {
    String queryString = &quot;select g.shipmentLabel as shipmentLabel from GenericShipment g where g.client.id = :clientId and g.shipmentId = :awb &quot; +
        &quot; AND (g.processingStatus is null or g.processingStatus &lt;&gt; &#39;DELETED&#39;) AND g.shipmentLabel is not null&quot;;

     val query = entityManager.createQuery(queryString, Byte[].class);

     query.setParameter(&quot;clientId&quot;, clientId);
     query.setParameter(&quot;awb&quot;, awb);

     return query.getResultStream().findFirst();

}

huangapple
  • 本文由 发表于 2020年9月25日 15:19:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/64059595.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定