Hibernate 6 强制转换查询参数

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

您好,有以下存储库:

@Repository
public interface FooRepository extends JpaRepository<Foo, Long>, JpaSpecificationExecutor<Foo> {

  @Query(
  """
      SELECT o FROM Foo o 
      JOIN Fee e on o.feeId = e.id 
      JOIN Fii i on e.fiiId = i.id 
      WHERE i.id = :fiiId 
      AND o.id IN (
        SELECT u.id 
        FROM Fuu u  
        JOIN Foo o2 on u.fooId = o2.id 
        WHERE u.something LIKE CONCAT('%', :param1, '_', :param2, '%')
      )""")
  List<OptionRule> findByFiiIdAndFuuLikeSomeComputed(
     Long fiiId, String param1, char param2);
}

备注:

u.something
是一个定义如下的列:

@Nullable
@Column(name = "SOMETHING", length = 25000)
private String something;

Hibernate 6 之前:

select foo0_.ID                 as id1_18_,
       ...
from FOO foo0_
         inner join FEE fee1_ on (foo0_.FEE_ID = fee1_.ID)
         inner join FII fii2_ on (fee1_.FII_ID = fii2_.ID)
where fii2_.ID = ?
and (foo0_.ID in (select foo4_.ID
       from FUU fuu3_
                inner join FOO foo4_
                           on (fuu3_.FOO_ID = fuu4_.ID)
       where foo3.SOMETHING like '%' || ? || '_' || ? || '%'))

升级到 Hibernate 6 后:

  1. "Could not convert 'java.lang.Character' to 'java.lang.String' using 'org.hibernate.type.descriptor.java.StringJavaType' to wrap"
    ,我通过将
    char
    替换为
    String
    来修复。
  2. 查询现在包括
    cast
    ,DB2 z/OS 不支持它。

查询:

select fo1_0.ID,
       ...
from FOO fo1_0
     join FEE fe1_0 on fo1_0.FEE_ID = fe1_0.ID
     join FII fi1_0 on fe1_0.FII_ID = fi1_0.ID
where fi1_0.ID = ?
and fo1_0.ID in (
    select 
      fo2_0.ID
    from FUU fu1_0
      join FOO fo2_0 on fu1_0.FOO_ID = fo2_0.ID
    where fu1_0.SOMETHING like cast('%' || cast(? as varchar(25000)) || '_' || cast(? as varchar(25000)) || '%' as varchar(32672)))

使用

cast(? as ...)
时如何去掉
org.hibernate.dialect.DB2zDialect
。我的 DB2 z/OS 12.xxx 好像不支持。

我试图在

SqlAstTranslator
SqlAstWalker
中找到合适的函数并覆盖它们,但生成的查询似乎不是来自这里。

编辑:

这是我目前正在使用的方言,但转换问题仍然存在:

旧版Db2Dialect.java

import org.hibernate.dialect.DB2zDialect;
import org.hibernate.dialect.pagination.LegacyDB2LimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;

public class LegacyDb2Dialect extends DB2zDialect {
  
  @Override
  public LimitHandler getLimitHandler() {
    return LegacyDB2LimitHandler.INSTANCE;
  }

  @Override
  public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
    return new StandardSqlAstTranslatorFactory() {
      @Override
      protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
          SessionFactoryImplementor sessionFactory, Statement statement) {
        return new LegacyDb2SqlAstTranslator<>(sessionFactory, statement, getVersion());
      }
    };
  }
}

旧版Db2SqlAstTranslator.java

import org.hibernate.dialect.DB2zSqlAstTranslator;
import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;

public class LegacyDb2SqlAstTranslator<T extends JdbcOperation> extends DB2zSqlAstTranslator<T> {

  public LegacyDb2SqlAstTranslator(
      SessionFactoryImplementor sessionFactory, Statement statement, DatabaseVersion version) {
    super(sessionFactory, statement, version);
  }

  @Override
  protected boolean supportsOffsetClause() {
    return false;
  }
}
java hibernate jpa hql dialect
1个回答
0
投票

Hibernate 可能无法提供一种直接的方法来覆盖此特定行为,因为转换通常是由方言如何描述各种数据类型和操作的兼容性决定的。
(所述方言规定了 Hibernate 如何将标准 JPQL 或 HQL(Hibernate 查询语言)转换为对于特定数据库在语法上正确的 SQL。请参阅“Hibernate 查询语言指南”)

此线程之后,您可以尝试解决该问题,无需修改Hibernate方言,方法是避免在子查询中使用串联和LIKE

操作,这些操作似乎会触发不需要的转换。
相反,在应用程序代码中预先计算
LIKE
 模式并将其作为单个参数传递:

@Query(""" SELECT o FROM Foo o JOIN Fee e on o.feeId = e.id JOIN Fii i on e.fiiId = i.id WHERE i.id = :fiiId AND o.id IN ( SELECT u.id FROM Fuu u JOIN Foo o2 on u.fooId = o2.id WHERE u.something LIKE :pattern )""") List<OptionRule> findByFiiIdAndFuuLikeSomeComputed(Long fiiId, String pattern);
在您的应用程序代码中,计算 

pattern

 参数:

String pattern = "%" + param1 + "_" + param2 + "%"; List<OptionRule> results = fooRepository.findByFiiIdAndFuuLikeSomeComputed(fiiId, pattern);
这将消除查询中连接和强制转换的需要,可能完全绕过该问题。

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