我正在尝试使用分页 EntityManager.createNativeQuery()
. 下面是我正在使用的基本代码:
var query = em.createNativeQuery("select distinct id from ... group by ... having ...");
List<BigDecimal> results = query
.setMaxResults(pageSize)
.setFirstResult(pageNumber * pageSize)
.getResultList();
什么时候 pageNumber
如果为0(第一页),我将得到预期的大小数列表:
但只要 pageNumber
>0(例如,第二页),我得到一个对象列表,这个列表中的每个对象似乎都包含两个bigdecimal,第一个包含db中的值,第二个bigdecimal似乎是此行的位置。
很明显我有个例外
java.lang.classcastexception:类[ljava.lang.object;不能强制转换为类java.math.bigdecimal
有人能解释一下这种差异吗,以及如何修正这种差异以总是返回一个大小数列表?谢谢您。
更新1:我创建了一个示例项目来重现这个问题。我只能用oracle数据库重现这个问题。在h2数据库中,它运行得很好,我一直得到一个与页码无关的大小数列表。
update-2:我还创建了一个带有h2的示例项目,它可以在没有这个问题的情况下工作。
4条答案
按热度按时间qqrboqgw1#
问题的根本原因在于如何在hibernate-oracle方言中实现分页。
有两种情况:
当我们有
setFirstResult(0)
将生成以下sql:如您所见,此查询返回的列列表与初始查询完全相同,因此您对这种情况没有问题。
当我们出发的时候
setFirstResult
在不0
值将生成以下sql:如您所见,此查询返回列列表,其中包含其他
rownum_
列,因此将此结果集强制转换为BigDecimal
.解决方案
如果您使用oracle 12c r1(12.1)或更高版本,您可以使用new row limiting子句以如下方式重写方言中的此行为:
然后使用它。
对于以下查询的“我的测试数据集”:
我得到了:
p、 另见hhh-12087
p、 另外,我简化了我对
AbstractLimitHandler
通过移除支票礼物FOR UPDATE
条款。我想在这种情况下,我们不会有什么好的检查。例如,对于以下情况:
休眠(使用
Oracle12cDialect
)将生成以下sql:如您所见,hibernate尝试通过移动
FOR UPDATE
到查询的结尾。但无论如何,我们会得到:jfewjypa2#
我模拟了你的咨询,一切正常。我用过
DataJpaTest
给我举个实体经理的例子,h2
内存数据库和JUnit 5
运行测试。见下表:我建议您检查您的本地查询。最好改用criteriaapi,让本机查询用于复杂咨询等极端情况。
更新
在作者发布了这个项目之后,我可以重现这个问题,它与甲骨文方言有关。由于未知原因,正在为第二个调用运行的查询是:
select * from ( select row_.*, rownum rownum_ from ( SELECT c.SHOP_ID FROM CUSTOMER c ) row_ where rownum <= ?) where rownum_ > ?
,这就是为什么它会生成一个bug,因为它查询的是两列而不是一列。不想要的是这个rownum
. 对于其他方言来说,没有这样的问题。我建议您尝试其他oracle方言版本,无论它们是否都有效,我最后的建议是尝试自己进行分页。
hzbexzde3#
您遇到的问题是,您的oracledialect向其选定的结果集添加了一列。它 Package 了您正在运行的查询,正如sternk的答案中所讨论的那样。
如果您使用的是hibernate sessionfactory和session接口,那么您要查找的函数将是“addscalar”方法。不幸的是,在纯jpa中似乎没有实现(请参阅这里提出的问题:jpa是否有与hibernate sqlquery.addscalar()等价的实现?)。
我希望您当前的实现在db2、h2、hsql、postgres、mysql(以及其他一些db引擎)中运行良好。但是,在oracle中,它向resultset中添加了一个行号列,这意味着hibernate从resultset中获得2列。在本例中,hibernate不实现任何查询解析,这意味着它只是将结果集解析到您的列表中。因为它得到2个值,所以它将它们转换为对象[],而不是bigdecimal。
作为警告,依赖jdbc驱动程序来提供预期的数据类型有点危险,因为hibernate会询问jdbc驱动程序它建议的数据类型。在本例中,它建议使用bigdecimal,但在某些条件下和某些实现将允许返回double或其他类型。
那你有几个选择。
你可以修改你的甲骨文方言(正如斯特恩克所建议的)。这将利用另一种oracle分页实现。
如果您不反对在jpa实现中使用hiberate特定方面,那么您可以利用jpa标准中没有提供的其他hibernate功能(请参阅以下代码…)
这样做的好处是可以显式地告诉hibnerate,您只对resultset的“id”列感兴趣,并且hibernate需要显式地将返回的对象转换为bigdecimal,如果jdbc驱动程序决定使用不同的类型作为默认值更合适的话。
e3bfsja24#
在对不同版本的spring库进行了大量的跟踪之后,我终于找到了问题所在。在我的一次尝试中,当我将spring数据共享库从v2.1.5.release更新到v2.1.6.release时,这个问题似乎已经消失了。我查阅了这个版本的changelog,这个bug与springdatacommons中的这个bug有关,是这个问题的根本原因。在升级spring数据共享库之后,我能够解决这个问题。