JPA coalesce / NVL,绑定参数和数据库索引用法

时间:2015-08-03 17:45:19

标签: oracle hibernate jpa coalesce nvl

当我们想在可空列上使用基于函数的索引时,Oracle中有一种使用谓词NVL(some_column, ' ') = ' '的通用方法,如下所示:

CREATE INDEX idx_some_index ON some_table (NVL(some_column, ' '));

JPA内置的谓词:

criteriaBuilder.equal(criteriaBuilder.coalesce(root.get("some_column"), " "), " ")

由hibernate实现生成的结果SQL谓词(也是由eclipse实现):

nvl(sometable0_.some_column, ?)=?

由于使用了第一个绑定参数,它不允许oracle使用基于函数的索引。因此,oracle使用FULL SCAN。

根据代码(LiteralExpression.render(...)),JPA实现为所有字符串文字(非数字文字)创建绑定参数。我认为它使用这种方法来避免可能的SQL注入...

使用数字列的类似情况可以正常工作:nvl(sometable0_.some_column, -1)=-1(第二个-1可以替换为criteriaBuilder.parameter(...),不会强制Oracle对每个新参数值进行硬解析。)

所以我的问题是:在我完全确定注射不可能的情况下,有没有合法的方法强制JPA不使用bind参数?

PS。我可以使用我自己的表达式实现(绕过条件构建器构建),如下所示:

public class UnsafeLiteralExpression<T> extends ExpressionImpl<T> implements Serializable {
    @SuppressWarnings({ "unchecked" })
    public UnsafeLiteralExpression(T literal) {
        this((Class<T>) determineClass( literal ), literal );
    }

    public UnsafeLiteralExpression(Class<T> type, T literal) {
        super(null, type );
        this.literal = literal;
    }

    //...

    public String render(CriteriaQueryCompiler.RenderingContext renderingContext) {
        return renderProjection(renderingContext);
    }

    //...
}

但我不认为这是正确的。

1 个答案:

答案 0 :(得分:1)

从11g开始,您可以使用虚拟列

   some_column_nvl VARCHAR2(10) GENERATED ALWAYS AS (NVL(some_column,' ')) VIRTUAL,

并在此列上使用普通索引

create index test_idx on test(some_column_nvl);

虚拟列处理数据库中的NVL,因此您可以简单查询:

 criteriaBuilder.equal(root.get("some_column_nvl"), " ")

使用索引

 |*  2 |   INDEX RANGE SCAN                  | TEST_IDX |     2 |       |     1   (0)| 00:00:01 |

获取some_column为空或NULL

的所有记录