Я столкнулся с проблемами запроса критериев 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?
Заранее спасибо.
Привет, @JoakimDanielson, я уверен, что SQL прав. Он возвращает мне нужные данные.
@Almett ваш запрос возвращает только 1 строку: "Bob" "information1" "www1" "2018-02-14 10:32:43". Возможно, опубликованные вами данные не являются фактическими данными. Также в вашем запросе измените 'ALice' на 'Alice', если это не опечатка или равенство не чувствительно к регистру.
Мне ужасно жаль, это «Алиса», а не «Алиса». Я изменил starttime Алисы (www3), чтобы он был больше, чем время начала Боба (www3). Теперь он возвращает тот же результат, что и мой. Спасибо за ваши усилия.
@JoakimDanielson Предполагая (имя, сервер) уникальным OP будет нормально, если нет ... могут возникнуть проблемы!




Я не знаком с 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 Рад помочь! Я имел в виду, что это может быть лучше с критериями гибернации. По моему опыту работы с фреймворками PHP, кажется, что JOIN лучше поддерживаются, чем подзапросы. При этом это должно было быть только дополнением к более сложному ответу, но было слишком богатым для комментария!
Мой совет - продолжайте попытки, смотрите больше в 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();
Этот код работает отлично. Великолепный! Браво! Большое тебе спасибо.
Но он работает только с Hibernate-5.2 +. А метод createCriteria() устарел в Hibernate-5.2 +. Если в createCriteria() написано множество критериев, то его нужно отремонтировать. В любом случае отличный ответ.
Вы уверены, что запрос sql действителен, я подозреваю, что он вернет ошибку, говоря, что подзапрос возвращает более одной строки или что-то подобное