问题的根本原因在于分页如何在休眠 oracle 方言中实现。
有两种情况:
- 当我们有以下sql时,将生成:
setFirstResult(0)
-- setMaxResults(5).setFirstResult(0)
select * from (
select test_id from TST_MY_TEST -- this is your initial query
)
where rownum <= 5;
如您所见,此查询返回的列列表与初始查询完全相同,因此您对此情况没有问题。
- 当我们设置 not 值时,将生成以下 sql:
setFirstResult
0
-- setMaxResults(5).setFirstResult(2)
select * from (
select row_.*, rownum rownum_
from (
select test_id from TST_MY_TEST -- this is your initial query
) row_
where rownum <= 5
)
where rownum_ > 2
如您所见,此查询返回包含其他列的列列表,因此您确实存在将此结果集强制转换为 .rownum_
BigDecimal
溶液
如果使用 Oracle 12c R1 (12.1) 或更高版本,则可以通过以下方式使用新的行限制子句在方言中覆盖此行为:
import org.hibernate.dialect.Oracle12cDialect;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.engine.spi.RowSelection;
public class MyOracleDialect extends Oracle12cDialect
{
private static final AbstractLimitHandler LIMIT_HANDLER = new AbstractLimitHandler() {
@Override
public String processSql(String sql, RowSelection selection) {
final boolean hasOffset = LimitHelper.hasFirstRow(selection);
final StringBuilder pagingSelect = new StringBuilder(sql.length() + 50);
pagingSelect.append(sql);
/*
see the documentation https://docs.oracle.com/database/121/SQLRF/statements_10002.htm#BABHFGAA
(Restrictions on the row_limiting_clause)
You cannot specify this clause with the for_update_clause.
*/
if (hasOffset) {
pagingSelect.append(" OFFSET ? ROWS");
}
pagingSelect.append(" FETCH NEXT ? ROWS ONLY");
return pagingSelect.toString();
}
@Override
public boolean supportsLimit() {
return true;
}
};
public MyOracleDialect()
{
}
@Override
public LimitHandler getLimitHandler() {
return LIMIT_HANDLER;
}
}
然后使用它。
<property name="hibernate.dialect">com.me.MyOracleDialect</property>
对于以下查询的测试数据集:
NativeQuery query = session.createNativeQuery(
"select test_id from TST_MY_TEST"
).setMaxResults(5).setFirstResult(2);
List<BigDecimal> results = query.getResultList();
我得到了:
Hibernate:
/* dynamic native SQL query */
select test_id from TST_MY_TEST
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY
val = 3
val = 4
val = 5
val = 6
val = 7
附言:另见HHH-12087
P.P.S 我简化了我对 检查礼物子句的实现。我认为在这种情况下和这次检查中,我们不会有任何好处。AbstractLimitHandler
FOR UPDATE
例如,对于以下情况:
NativeQuery query = session.createNativeQuery(
"select test_id from TST_MY_TEST FOR UPDATE OF test_id"
).setMaxResults(5).setFirstResult(2);
休眠 (with ) 将生成以下 sql:Oracle12cDialect
/* dynamic native SQL query */
select * from (
select
row_.*,
rownum rownum_
from (
select test_id from TST_MY_TEST -- initial sql without FOR UPDATE clause
) row_
where rownum <= 5
)
where rownum_ > 2
FOR UPDATE OF test_id -- moved for_update_clause
如您所见,休眠尝试通过移动到查询的末尾来修复查询。但无论如何,我们将得到:FOR UPDATE
ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.