为什么Hibernate 6.2将JPQL equals运算符转换为IN运算符?

pbossiut  于 2023-08-06  发布在  其他
关注(0)|答案(1)|浏览(84)

我有一个Sping Boot 3.1.1 / Hibernate 6.2应用程序,它使用以下驱动程序连接到Oracle 12 c数据库:

<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <scope>runtime</scope>
</dependency>

字符串
此应用程序具有以下存储库接口和类:

public interface SomeEntityRepository extends Repository<SomeEntity, SomeEntityId> {
  @Query("""
      FROM SomeEntity
      WHERE id = :id
        AND description is not null
      """)
  Optional<SomeEntity> find(SomeEntityId id);
}

x

@Embeddable
public class SomeEntityId {
    private Long idX;
    private Long idY;
    private Long idZ;
}
@Entity
public class SomeEntity {
    @EmbeddedId
    private SomeEntityId id;
    private String description;
}

的字符串
当执行find查询时,Hibernate 6.2将JPQL转换为以下SQL:

Hibernate: 
    select
        s1_0.idx,
        s1_0.idy,
        s1_0.idz,
        s1_0.description 
    from
        some_entity s1_0 
    where
        (
            s1_0.idx,s1_0.idy,s1_0.idz
        ) in (select
            ?,?,? 
        from
            dual) 
        and s1_0.description is not null


我想知道为什么Hibernate 6.2将id = :id转换为子查询,而不是像Hibernate 5那样逐个字段比较复合主键?
我尝试在Sping Boot 2.7.13 / Hibernate 5.6中执行相同的代码,JPQL按预期进行了翻译:

Hibernate: 
    select
        someentity0_.idx as idx1_1_,
        someentity0_.idy as idy2_1_,
        someentity0_.idz as idz3_1_,
        someentity0_.description as description4_1_ 
    from
        some_entity someentity0_ 
    where
        someentity0_.idx=? 
        and someentity0_.idy=? 
        and someentity0_.idz=? 
        and (
            someentity0_.description is not null
        )


使用AND=单独比较复合PK属性,而不是使用IN运算符比较的Hibernate 6.2:

...
        (
            s1_0.idx,s1_0.idy,s1_0.idz
        ) in (select
            ?,?,? 
        from
            dual) 
...

djp7away

djp7away1#

首先,当通过id检索实体时,你很可能不需要使用@Query注解的方法,请在这里检查一些想法:JPA Query with several different @Id columns
您描述的行为在HBN6中引入,相关代码位于AbstractSqlAstTranslator#visitRelationalPredicate方法中:

else if ( needsTupleComparisonEmulation( operator ) ) {
    rhsTuple = SqlTupleContainer.getSqlTuple( rhsExpression );
    assert rhsTuple != null;
    // Some DBs like Oracle support tuples only for the IN subquery predicate
    if ( ( operator == ComparisonOperator.EQUAL || operator == ComparisonOperator.NOT_EQUAL ) && supportsRowValueConstructorSyntaxInInSubQuery() ) {
        comparisonPredicate.getLeftHandExpression().accept( this );
        if ( operator == ComparisonOperator.NOT_EQUAL ) {
            appendSql( " not" );
        }
        appendSql( " in (" );
        renderExpressionsAsSubquery( rhsTuple.getExpressions() );
        appendSql( CLOSE_PARENTHESIS );
    }
    else {
        emulateTupleComparison(
                lhsTuple.getExpressions(),
                rhsTuple.getExpressions(),
                operator,
                true
        );
    }
}

字符串
在这种情况下,我认为HBN开发人员不熟悉Oracle语法,即上述代码段中的注解Oracle support tuples only for the IN subquery predicate不正确:标准的Oracle方法来编写比较元组的表达式如下(注意双括号):

(s1_0.idx,s1_0.idy,s1_0.idz) IN ((?,?,?))


如果您遇到性能问题,我建议您在HBN Jira中提出一个bug

相关问题