Группа критериев jpa по совокупным значениям

Мне нужен запрос для преобразования времени (миллисек) в почасовое распределение.

в моей таблице есть столбец с именем timestamp: значение в миллисекундах. чтобы сгруппировать по часам, мне нужно сделать следующее: разделить на 1000 -> разделить на 3600 секунд -> часы -> мод 24 -> получить час дня, когда были созданы записи.

временная метка длинная:

    public static volatile SingularAttribute<VersionJpa, Long> timestamp;

Затем я могу отобразить результаты в дневном распределении. Однако это не работает, группа не работает, и я получаю полный список, не сгруппированный и не подсчитанный.

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Tuple> cq = cb.createTupleQuery();
    Root<VersionJpa> root = cq.from(VersionJpa.class);
    cq.where(p);
    Expression<Integer> group = cb.mod(cb.toInteger(cb.quot(root.get(VersionJpa_.timestamp), 3600000)),24);
    cq.multiselect(group, cb.count(root));
    cq.groupBy(group);
    TypedQuery<Tuple> tq = em.createQuery(cq);
    return tq.getResultList();

этот SQL (mysql) дает правильные результаты. есть ли эквивалентный пол в JPA? Пробовал как.(Целое число) || toInteger()

    select mod(floor(stamp/3600000), 24) , count(*) from versions group by mod(floor(stamp/3600000), 24);

Я обнаружил, что во время модульного теста (на Derby) это работает хорошо, но в более продуктивной среде, такой как установка (MySQL), он не преобразует самый внутренний агрегат в целое число, поэтому группа выполняется с плавающими значениями, а не с 24 (0). , 1, .. 23) потенциальные значения (возврат тысяч групп).

    Expression<Integer> hours = cb.mod(cb.quot(root.get(VersionJpa_.timestamp).as(Integer.class), 3600000).as(Integer.class),24).as(Integer.class);
    cq.multiselect(hours, cb.count(root));
    cq.groupBy(hours);

добавление поддержки и тестирования PostgreSQL приводит к еще одной ошибке, которая заставляет меня думать, что мой запрос неверен:

    RuntimeException Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: org.postgresql.util.PSQLException: ERROR: column "versions.stamp" must appear in the GROUP BY clause or be used in an aggregate function Position: 13 Error Code: 0 Call: SELECT MOD((STAMP / ?), ?), COUNT(ID) FROM VERSIONS GROUP BY MOD((STAMP / ?), ?) LIMIT ? OFFSET ? bind => [6 parameters bound] Query: TupleQuery(referenceClass=VersionJpa sql="SELECT MOD((STAMP / ?), ?), COUNT(ID) FROM VERSIONS GROUP BY MOD((STAMP / ?), ?) LIMIT ? OFFSET ?")

любая помощь приветствуется.


person Gadi    schedule 02.08.2013    source источник


Ответы (2)


Вы можете использовать метод function() для вызова любой функции базы данных.

См. http://java-persistence-performance.blogspot.com/2012/05/jpql-vs-sql-have-both-with-eclipselink.html

Postgres, кажется, больше жалуется на использование функции в группе, в JPQL вы можете попробовать,

Select mod((v.timestamp / 3600000), 24) h, count(v) from Version v group by h
person James    schedule 07.08.2013
comment
спасибо, я попробовал это с полом, и это не сработало. Мне нужно работать с критериями, потому что я добавляю фильтрацию/выбираю более динамично. это ошибка eclipse или я просто использую неправильный синтаксис? - person Gadi; 08.08.2013

для тех, кто когда-либо столкнется с этой проблемой, я нашел обходной путь, основанный на столбцах базы данных, а не на сложных запросах. напоминание, проблема заключалась в том, что поддержка разных поставщиков не гарантируется. он работает на Derby, но не работает на MySQL.

Я создал новый столбец для каждой интересующей меня метрики:

  • год
  • месяц
  • день недели
  • день месяца
  • час дня (24)

а затем заполнил его календарем, инициализированным с настройками локализации и системным временем.

    Calendar now = Calendar.getInstance();
    now.setTimeInMillis(timestamp);
    this.year = now.get(Calendar.YEAR);
    logger.trace("Year : [{}]", year);
    this.month = now.get(Calendar.MONTH);
    logger.trace("Month : [{}]", month);
    this.weekOfYear = now.get(Calendar.WEEK_OF_YEAR);
    logger.trace("Week of Year : [{}]", weekOfYear);
    this.dayOfMonth = now.get(Calendar.DAY_OF_MONTH);
    logger.trace("Day of Month : [{}]", dayOfMonth);
    this.dayOfWeek = now.get(Calendar.DAY_OF_WEEK);
    logger.trace("Day of Week : [{}]", dayOfWeek);
    this.hourOfDay = now.get(Calendar.HOUR_OF_DAY);
    logger.trace("Hour of Day : [{}]", hourOfDay);

надеюсь, это поможет кому-то. ваше здоровье.

person Gadi    schedule 09.08.2013