您好,有以下存储库:
@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 后:
"Could not convert 'java.lang.Character' to 'java.lang.String' using 'org.hibernate.type.descriptor.java.StringJavaType' to wrap"
,我通过将 char
替换为 String
来修复。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;
}
}
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);
这将消除查询中连接和强制转换的需要,可能完全绕过该问题。