使用 Criteria、DB2zDialect 和 Pagination 时出现 JpaSystemException

问题描述 投票:0回答:1

所以我对 spring-boot 应用程序进行了以下设置:

org.springframework.boot:spring-boot-starter:3.2.4
com.ibm.db2:jcc:11.5.8.0
java.version=21
org.hibernate.orm:hibernate-core:6.4.4.Final

连接到 DB2 z/OS 数据库 (DSN12015) v12.015。

我有以下方言:

import org.hibernate.dialect.DB2zDialect;
import org.hibernate.dialect.pagination.LegacyDB2LimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;

public class Db2zOsDialect extends DB2zDialect {
  @Override
  public SequenceInformationExtractor getSequenceInformationExtractor() {
    return LegacyDb2zOsSequenceInformationExtractor.INSTANCE;
  }

  @Override
  public LimitHandler getLimitHandler() {
    return LegacyDB2LimitHandler.INSTANCE;
  }
}

我有以下实体:

@Table(name = "\"KEY\"")
public class Key {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID", unique = true, nullable = false)
  private Long id;

  @Column(name = "\"VALUE\"", nullable = false, length = 100)
  private String value;

  ...
}

以及以下存储库Repository:

@Repository
interface KeyRepository extends JpaRepository<Key, Long>, JpaSpecificationExecutor<Key> {}

我现在想使用规范来阅读它:

final String toSearchFor = "%foo%"
Specification<User> spec = (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("value"), toSearchFor);
final PageRequest pageRequest = PageRequest.of(0, 100, Sort.by(Direction.DESC, "id"));
repo.findAll(specification, pageRequest)

使用完全相同的代码而不使用

PageRequest
效果完美。但一旦分页出现,它就会失败。 我发现自从我们从 spring-boot 2.X 更新以来,查询发生了变化,由 JPA 和 hibernate 在内部执行:

原始查询:

select key0_.ID                        as id1_0_,
       key0_."VALUE"                   as value_2_0_,
       ...
from MY_SCHEMA."KEY" key0_
where key0_."VALUE" like '%foo%'
order by key0_.ID desc
fetch first 100 rows only;

新查询:

select k1_0.ID,
       k1_0."VALUE",
       ...
from MY_SCHEMA."KEY" k1_0
where k1_0."VALUE" like '%foo%'
order by k1_0.ID desc
offset 0 rows fetch first 100 rows only;

如果我通过 SQL 工具针对数据库执行这两个查询,它们都会运行

到目前为止我发现它不会使用

Db2zOsDialect
而是执行其他操作,其中包括
offset
。这发生在
DeferredResultSetAccess
的构造函数中:

例外:

org.hibernate.exception.GenericJDBCException: JDBC exception executing SQL [select k1_0.ID, k1_0."VALUE", ... from MY_SCHEMA."KEY" k1_0 where k1_0."VALUE" like '%foo%' order by k1_0.ID desc offset 0 rows fetch first 100 rows only] [DB2 SQL Error: SQLCODE=-4743, SQLSTATE=56038, SQLERRMC=null, DRIVER=4.32.28] [n/a]

错误日志:

2024-04-04T09:36:41.769+02:00 ERROR 22710 --- [nio-9080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper   : DB2 SQL Error: SQLCODE=-4743, SQLSTATE=56038, SQLERRMC=null, DRIVER=4.32.28
2024-04-04T09:36:41.769+02:00 ERROR 22710 --- [nio-9080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper   : DB2 SQL Error: SQLCODE=-516, SQLSTATE=26501, SQLERRMC=null, DRIVER=4.32.28
2024-04-04T09:36:41.769+02:00 ERROR 22710 --- [nio-9080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper   : DB2 SQL Error: SQLCODE=-514, SQLSTATE=26501, SQLERRMC=SQL_CURLH200C1, DRIVER=4.32.28

问题:

  • 为什么使用“offset”而不是我的 LimitHandler
  • 我知道 hibernate 6 上有“别名”的更新,但为什么它会产生影响
  • 这是内部 JPA/Hibernate/JDBC 问题吗?

编辑:

方言被接受了,尽管如此,Hibernate 做了一些见不得人的事情:

Spring-Boot 配置

application.yml
:

spring:
  jpa:
    hibernate:
      ddl-auto: none
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        dialect: com.my.package.Db2zOsDialect
        default_schema: MY_SCHEMA
        criteria:
          literal_handling_mode: inline
        hbm2ddl:
          halt_on_error: true
    open-in-view: false
  datasource:
    url: jdbc:db2://someserver:1234/DBNAME:sslConnection=true;currentSchema=MY_SCHEMA;useUnicode=yes;characterEncoding=UTF-8;
    username: someuser
    password: sumepassword
    driver-class-name: com.ibm.db2.jcc.DB2Driver

编辑2:

我从代码中删除了

Specification
Sort
,但仍然收到错误:

repo.findAll(PageRequest.of(0, 100))

查询也变得更加简单:

select k1_0.ID, k1_0."VALUE" from MY_SCHEMA."KEY" k1_0 offset 0 rows fetch first 100 rows only

我现在甚至尝试使用

com.ibm.db2:jcc:11.5.9.0
作为依赖项来升级到最新的 JDBC 驱动程序。但这现在产生了一个新问题:

com.ibm.db2.jcc.am.SqlInvalidAuthorizationSpecException: [jcc][t4][2010][11246][4.33.31] Connection authorization failure occurred.  Reason: Local security service non-retryable error. ERRORCODE=-4214, SQLSTATE=28000

我也降级到旧版本的 JDBC 驱动程序,但问题仍然相同。

java spring hibernate jdbc db2-zos
1个回答
0
投票

查看休眠代码,我最终看到

DB2SqlAstTranslator
,它也是使用
Dialect
方法从
getSqlAstTranslatorFactory
返回的。
DB2SqlAstTranslator
是最终创建查询的内容。这可能是由于 Hibernate 6+ 中整个 AST 处理的重构所致。

DB2SqlAstTranslator
有一个
supportsOffsetClause
方法,仅检查版本并返回
true
。当返回
true
时,它将进入生成带有
offset 0 ...
的查询的路径,如果它返回
false
,它将返回较旧的查询。

您可以重写该方法以返回

false
,然后在您的
Db2zOsDialect
中返回您的自定义
AstTranslatorFactory

但奇怪的是,在普通工具中查询执行成功,但在 JPA 中执行失败。错误代码表明准备语句时存在一些问题(因为我怀疑这是一些虚拟 SQL,而不是原始 SQL,这很难确定)。您可能还想向 Hibernate 注册问题。

© www.soinside.com 2019 - 2024. All rights reserved.