GORM - 选择带订单和锁定的最大记录 - ORA-02014

时间:2013-07-17 20:54:52

标签: oracle hibernate grails gorm

如何使用GORM完成以下查询?

select * 
from T where id in 
(
    SELECT id
    FROM (
        SELECT *
        FROM T
        WHERE X is NULL
        ORDER BY Y DESC
        )
    WHERE ROWNUM <= 1
)
FOR UPDATE;

我正在尝试的方法调用如下所示:

T.findByXIsNull(sort: "Y", order:"desc", lock: true)

但是,我收到以下错误(Oracle 11gR2): ORA-02014:无法从DISTINCT,GROUP BY等视图中选择FOR UPDATE

我认为它失败的原因是因为Hibernate将其转换为以下查询:

SELECT *
FROM (
    SELECT *
    FROM T
    WHERE X is NULL
    ORDER BY Y DESC
    )
WHERE ROWNUM <= 1 FOR UPDATE;

此查询尝试直接在限制rownum的子句上应用FOR UPDATE。需要一个包装器select语句来应用FOR UPDATE,就像在我的例子中一样(在这里讨论:How to solve ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY)。如何实现这一目标?

UPDATE1

在指定order byrownnum <= ?时生成查询时,这似乎是GORM / Hibernate中的错误。以下两项单独工作:

T.findByXIsNull(sort: "Y", order:"desc")
T.findByXIsNull(lock: true)

但是他们一起T.findByXIsNull(sort: "Y", order:"desc", lock:true)因ORA-02014错误而失败。修复将是Hibernate生成SQL,正如我在本文顶部描述的那样,它将锁包装到另一个外部select语句中。但是,可能有一个我不知道的解决方法。

1 个答案:

答案 0 :(得分:1)

AFAIK findBy*不支持分页和订单参数,因为它始终会返回第一个匹配结果。

如果您想使用排序,则必须使用findAllBy*,然后选择第一行进行锁定。

使用findBy我会尝试

//To avoid the infinitesimal chance of dirtiness between fetching and locking.
def t = T.findByXIsNull([lock: true])
//Round-about an unliked way 
def t = T.lock(T.findByXIsNull()?.id) 

//Or easier
def t = T.findByXIsNull()
t.lock()

findAllBy与分页参数一起使用:

def t = T.findAllByXIsNull(sort: "Y", order:"desc", max: 1, lock: true)

未经测试使用Oracle db