Подтвердить что ты не робот

Спящий режим с нулями

Спящий режим, используемый с DB PostgreSQL, при заказе дескриптора столбцом ставит нулевые значения выше, чем не null.

Стандарт SQL99 предлагает ключевое слово "NULLS LAST", чтобы объявить, что нулевые значения должны быть помещены ниже нуля.

Может ли поведение "NULLS LAST" быть достигнуто с использованием API-интерфейсов Hibernate?

4b9b3361

Ответ 1

Учитывая, что HHH-465 не фиксирован и не будет исправлен в ближайшем будущем по причинам, указанным Стивом Эберсоле, вашим лучшим вариантом было бы использовать CustomNullsFirstInterceptor, прикрепленный к проблеме, либо глобально, либо специально для изменения инструкции SQL.

Я размещаю его ниже для читателей (кредиты Эмилио Дольче):

public class CustomNullsFirstInterceptor extends EmptyInterceptor {

    private static final long serialVersionUID = -3156853534261313031L;

    private static final String ORDER_BY_TOKEN = "order by";

    public String onPrepareStatement(String sql) {

        int orderByStart = sql.toLowerCase().indexOf(ORDER_BY_TOKEN);
        if (orderByStart == -1) {
            return super.onPrepareStatement(sql);
        }
        orderByStart += ORDER_BY_TOKEN.length() + 1;
        int orderByEnd = sql.indexOf(")", orderByStart);
        if (orderByEnd == -1) {
            orderByEnd = sql.indexOf(" UNION ", orderByStart);
            if (orderByEnd == -1) {
                orderByEnd = sql.length();
            }
        }
        String orderByContent = sql.substring(orderByStart, orderByEnd);
        String[] orderByNames = orderByContent.split("\\,");
        for (int i=0; i<orderByNames.length; i++) {
            if (orderByNames[i].trim().length() > 0) {
                if (orderByNames[i].trim().toLowerCase().endsWith("desc")) {
                    orderByNames[i] += " NULLS LAST";
                } else {
                    orderByNames[i] += " NULLS FIRST";
                }
            }
        }
        orderByContent = StringUtils.join(orderByNames, ",");
        sql = sql.substring(0, orderByStart) + orderByContent + sql.substring(orderByEnd); 
        return super.onPrepareStatement(sql);
    }

}

Ответ 2

Эта функция была реализована во время выпусков Hibernate 4.2.x и 4.3.x, как упоминалось ранее.

Его можно использовать, например:

Criteria criteria = ...;
criteria.addOrder( Order.desc( "name" ).nulls(NullPrecedence.FIRST) );

Hibernate v4.3 javadocs менее осмысленны здесь.

Ответ 3

Здесь мое обновление класса (Pascal Thivent):

for (int i = 0; i < orderByNames.length; i++) {
    if (orderByNames[i].trim().length() > 0) {
        String orderName = orderByNames[i].trim().toLowerCase();
        if (orderName.contains("desc")) {
            orderByNames[i] = orderName.replace("desc", "desc NULLS LAST");
        } else {
            orderByNames[i] = orderName.replace("asc", "asc NULLS FIRST");
        }
    }
}

Это устраняет проблему:

Это прерывается, если sql имеет лимит/смещение после заказа - Sathish Apr 1 '11 at 14:52

Также здесь вы можете использовать это в JPA (спящий режим):

Session session = entityManager.unwrap(Session.class);
Session nullsSortingProperlySession = null;
try {
    // perform a query guaranteeing that nulls will sort last
    nullsSortingProperlySession = session.getSessionFactory().withOptions()
        .interceptor(new GuaranteeNullsFirstInterceptor())
        .openSession();
} finally {
    // release the session, or the db connections will spiral
    try {
        if (nullsSortingProperlySession != null) {
            nullsSortingProperlySession.close();
        }
    } catch (Exception e) {
        logger.error("Error closing session", e);
    }
}

Я тестировал это на postgres, и он исправляет ошибку "nulls are more than non-nulls", которые у нас были.

Ответ 4

Другой вариант, если вы создаете SQL "на лету" и не используете API критериев:

ORDER BY COALESCE (, '0') [ASC | DESC]

Это работает либо для varchar, либо для числовых столбцов.

Ответ 5

Вы можете настроить "nulls first" / "nulls last" в свойствах hibernate, чтобы по умолчанию был выбран любой критерий: hibernate.order_by.default_null_ordering=last (или =first).

Подробнее см. этот hibernate commit.