Как реализовать этот множественный выбор и запрос where с критериями Hibernate?

Я столкнулся с проблемами запроса критериев Hibernate.

Таблица данных DataStatus выглядит следующим образом:

|-------------|--------------|--------------|---------------------|
|     name    |     info     |    server    |      starttime      |
|-------------|--------------|--------------|---------------------|
|     Bob     | information1 |     www1     | 2018-02-14 10:32:43 |
|     Alice   | information2 |     www3     | 2018-02-14 17:34:43 |
|     Bob     | information3 |     www2     | 2018-02-14 10:32:43 |
|     Alice   | information4 |     www1     | 2018-02-14 11:25:51 |
|     Alice   | information5 |     www2     | 2018-02-14 08:42:25 |
|     Bob     | information6 |     www3     | 2018-02-14 10:32:43 |
|-------------|--------------|--------------|---------------------|

Запрос выглядит так:

SELECT * FROM DataStatus sts 
WHERE sts.server IS NOT NULL 
AND sts.name = 'Bob' 
AND sts.starttime < (
   SELECT starttime FROM DataStatus 
   WHERE name = 'Alice' AND server = sts.server);

И результат выглядит так:

|-------------|--------------|--------------|---------------------|
|     name    |     info     |    server    |      starttime      |
|-------------|--------------|--------------|---------------------|
|     Bob     | information1 |     www1     | 2018-02-14 10:32:43 |
|     Bob     | information6 |     www3     | 2018-02-14 10:32:43 |
|-------------|--------------|--------------|---------------------|

Я пробовал что-то вроде ниже:

Criteria criteria = session.CreateCriteria(DataStatus.class);
criteria.add(
    Restrictions.and(
        criteria.add(Restrictions.isNotNull("server")),
        criteria.add(Restrictions.eq("name", "Bob")),
        criteria.add(Restrictions.lt("starttime", ))
    )
);

Я понятия не имею, как реализовать этот вложенный запрос where и select с критериями Hibernate?

Заранее спасибо.

Вы уверены, что запрос sql действителен, я подозреваю, что он вернет ошибку, говоря, что подзапрос возвращает более одной строки или что-то подобное

Joakim Danielson 03.11.2018 12:44

Привет, @JoakimDanielson, я уверен, что SQL прав. Он возвращает мне нужные данные.

Almett 03.11.2018 12:50

@Almett ваш запрос возвращает только 1 строку: "Bob" "information1" "www1" "2018-02-14 10:32:43". Возможно, опубликованные вами данные не являются фактическими данными. Также в вашем запросе измените 'ALice' на 'Alice', если это не опечатка или равенство не чувствительно к регистру.

forpas 03.11.2018 13:17

Мне ужасно жаль, это «Алиса», а не «Алиса». Я изменил starttime Алисы (www3), чтобы он был больше, чем время начала Боба (www3). Теперь он возвращает тот же результат, что и мой. Спасибо за ваши усилия.

Almett 03.11.2018 13:45

@JoakimDanielson Предполагая (имя, сервер) уникальным OP будет нормально, если нет ... могут возникнуть проблемы!

Arth 05.11.2018 19:29
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
5
1 566
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не знаком с Hibernate, но вы можете переписать запрос, чтобы избежать подзапроса

   SELECT bob.* /** Only include cols from Bob records */
     FROM DataStatus bob 

          /** Only include rows with a later Alice record on the same server */   
     JOIN DataStatus alice
       ON alice.name = 'Alice'
      AND alice.server = bob.server
      AND alice.starttime > bob.starttime

    WHERE bob.name = 'Bob'

Вы можете найти это лучше с синтаксисом Hibernate.

N.B. Предполагается, что у вас есть максимум одна запись для каждого имени и сервера. Это может быть выполнено с помощью УНИКАЛЬНОГО КЛЮЧА на (имя, сервер)

Это предположение основано на условии WHERE {bob server row starttime} < SELECT {alice server row starttime} из вашего исходного запроса, что на самом деле не имеет смысла, если SELECT может возвращать более одной строки.

Привет @Arth, Спасибо за большие усилия. Да, он работает с синтаксисом Hibernate, но моим требованием к проекту было использование критериев Hibernate. Ваша глубокая мысль по этому поводу выглядит очень впечатляюще, я постараюсь переделать SQL с вашей подсказкой. Спасибо!

Almett 06.11.2018 15:23

@Almett Рад помочь! Я имел в виду, что это может быть лучше с критериями гибернации. По моему опыту работы с фреймворками PHP, кажется, что JOIN лучше поддерживаются, чем подзапросы. При этом это должно было быть только дополнением к более сложному ответу, но было слишком богатым для комментария!

Arth 06.11.2018 16:36
Ответ принят как подходящий

Мой совет - продолжайте попытки, смотрите больше в Javadocs, экспериментируйте в своей IDE. И если вы сдадитесь, попробуйте следующее (при условии, что ваша таблица смоделирована в классе DataStatus):

CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<DataStatus> criteriaQuery = criteriaBuilder.createQuery(DataStatus.class);
Root<DataStatus> root = criteriaQuery.from(DataStatus.class);

Subquery<Date> subquery = criteriaQuery.subquery(Date.class);
Root<DataStatus> innerRoot = subquery.from(DataStatus.class);

subquery.select(innerRoot.get("startTime"))
        .where(criteriaBuilder.and(criteriaBuilder.equal(innerRoot.get("name"), "Alice"),
                criteriaBuilder.equal(innerRoot.get("server"), root.get("server"))));

criteriaQuery.select(root).where(
        criteriaBuilder.and( criteriaBuilder.isNotNull( root.get( "server" ) ),
                criteriaBuilder.equal(root.get("name"), "Bob" ),
                criteriaBuilder.lessThan(root.<Date> get("startTime"), subquery)

        ) );

Query<DataStatus> query = session.createQuery(criteriaQuery);
List<DataStatus> resultList = query.getResultList();

Этот код работает отлично. Великолепный! Браво! Большое тебе спасибо.

Almett 06.11.2018 15:19

Но он работает только с Hibernate-5.2 +. А метод createCriteria() устарел в Hibernate-5.2 +. Если в createCriteria() написано множество критериев, то его нужно отремонтировать. В любом случае отличный ответ.

Almett 06.11.2018 15:25

Другие вопросы по теме