Ниже приведены некоторые таблицы в моем приложении продукта, в которых мне нужно выполнить оператор Case в соответствии со значениями данных.
class Payment implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@JoinColumn(name = "payee_id", referencedColumnName = "id", nullable = true)
@OneToOne(optional = true)
private ProductPayee payeeId;
@JoinColumn(name = "allocation_id", referencedColumnName = "id", nullable = true)
@OneToOne(optional = true)
private ProductAllocation allocationId; // can have value only if no payeeId(ie, when payeeId = null) else null
@JoinColumn(name = "user_id", referencedColumnName = "id", nullable = true)
@OneToOne(optional = true)
private Recipient userId; // if no payeeId
@Column(name = "amount")
private Double amount;
}
class ProductPayee implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@JoinColumn(name = "allocation_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = false)
private ProductAllocation allocationId;
@JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = false)
private Recipient userId;
}
class ProductAllocation implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@JoinColumn(name = "product_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = true)
private Product productId;
@Column(name = "amount")
private Double amount;
}
class GroupLink implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@JoinColumn(name = "group_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = true)
private Group groupId;
@JoinColumn(name = "product_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = false)
private Product productId;
}
class GroupStaff implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@JoinColumn(name = "staff_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = false)
private Staff staffId;
@JoinColumn(name = "group_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = true)
private Group groupId;
}
class ProductStaff implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@JoinColumn(name = "staff_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = false)
private Staff staffId;
@JoinColumn(name = "allocation_id", referencedColumnName = "id", nullable = false)
@OneToOne(optional = false)
private ProductAllocation allocationId;
@Column(name = "type")
private int type;
}
Получатель, Персонал и Продукт аналогичны обычным таблицам «Персонал» и «Предмет».
Ниже приведен запрос, который я выполняю для получения данных, в которых таблица «Платежи» имеет либо payeeId со значением, либо AllocationId со значением.
Таким образом, (1) если идентификатор payeeId присутствует, то значениеlocationId будет иметь значение null и проверьте значение в ProductPayee, или (2) в противном случае значение locationId присутствует, а затем значение payeeId будет иметь значение null и проверьте таблицу ProductAllocation.
Query query = session.createQuery("FROM Payment where " +
"(case when payeeId != null then payeeId.allocationId.productId.id else allocationId.productId.id end in (SELECT productId.id from GroupLink where groupId.id in (SELECT groupId.id from GroupStaff where staffId.id = :staffId)) " +
"OR case when payeeId != null then payeeId.allocationId.id else allocationId.id end in (SELECT allocationId.id FROM ProductStaff WHERE staffId.id = :staffId and type = :resType))) " +
"order by date desc");
Мне нужно избегать столбца с нулевым значением в предложенииwhere из двух столбцов и искать одно и то же значение в обоих столбцах, которое никогда не является нулевым, любой из двух столбцов всегда имеет нулевое значение.
Вышеуказанный запрос HQL не дает ожидаемого результата при выполнении. Как мне написать это правильно, избегая нулевого регистра в HQL?
Пробовал с COALESCE(payeeId.allocationId.productId.id, allocationId.productId.id) in (Select ...)
, но все равно большая часть записей отсутствует.
Я хочу COALESCE(payeeId.allocationId.productId.id,locationId.productId.id), но он будет работать, только если это просто COALESCE(payeeId,locationId). Итак, проблема в том, что если значение locationId равно NULL, то тогдаlocationId.productId. id вернет ошибку. Оператор COALESCE завершит работу, аналогично, если payeeId NULL, то payeeId.allocationId.productId.id приведет к ошибке. Как я могу преодолеть эту ситуацию?
Вам нужно придерживаться только HQL? Разве это не может быть собственный запрос?
@FrancescoPoli Думаю, я предоставил здесь необходимую информацию... обновлю больше, если хочешь..
@Asgar Я не использую собственные запросы... могу подумать, нет ли в них особых различий...
Если вы посмотрите на запросы, выполняемые при выполнении этого оператора, можно увидеть собственные запросы. Так почему бы не попробовать это самим. Они не такие уж и сложные
@Asgar Так можешь ли ты помочь мне написать выше, используя собственный запрос??
@FrancescoPoli Я обновил его, добавив больше таблиц... Получатель, Персонал и Продукт похожи на обычные таблицы Person и Item...
@Asgar Мне нужно COALESCE(payeeId.allocationId.productId.id,locationId.productId.id), но это будет работать, только если это просто COALESCE(payeeId,locationId)
Почему бы вам не предоставить примеры таблиц базы данных, примеры данных и желаемый результат? Таким образом, кто-то может помочь и вам с запросом.
если вы используете Hibernate 6.0+, могу ли я предложить использовать предложение UNION
в запросе hql вместо case
, чтобы помочь разделить различные ограничения, которые необходимо использовать, если поля payeeId
или allocationId
имеют значение null или нет? Я попытался переписать ваш запрос, используя этот подход, и получил следующее:
"select p from Payment p where p.payeeId is not null and p.allocationId is null and "
+ "( "
+ "p.payeeId.allocationId.productId.id in (SELECT productId.id from GroupLink where groupId.id in (SELECT groupId.id from GroupStaff where staffId.id = :staffId)) "
+ "or "
+ "p.payeeId.allocationId.id in (SELECT allocationId.id FROM ProductStaff WHERE staffId.id = :staffId and type = :resType) "
+ ")"
+ "union "
+ "select p from Payment p where p.payeeId is null and p.allocationId is not null and "
+ "( "
+ "p.allocationId.productId.id in (SELECT productId.id from GroupLink where groupId.id in (SELECT groupId.id from GroupStaff where staffId.id = :staffId)) "
+ "or "
+ "p.allocationId.id in (SELECT allocationId.id FROM ProductStaff WHERE staffId.id = :staffId and type = :resType)"
+ ")"
Как видите, существует union
из двух подзапросов: первый возникает, когда payeeId
не имеет значения NULL, а второй — когда allocationId
не имеет значения NULL. Я просто выполнил условие, чтобы оно не было исключительно нулевым.
В вопросе вы говорите, что «запрос не дает соответствующего результата при выполнении»: какие записи отсутствуют или возвращаются, когда они не должны?
Обновление после комментария «объединиться».
Вы можете попробовать написать соединения в запросе и применить предложение объединения к нужным атрибутам, например:
"select p from Payment p "
+ "left join p.payeeId pp left join pp.allocationId ppa left join ppa.productId ppap "
+ "left join p.allocationId pa left join pa.productId pap "
+ "where coalesce(ppap.id, pap.id) in "
+ " (SELECT productId.id from GroupLink where groupId.id in (SELECT groupId.id from GroupStaff where staffId.id = :staffId)) "
+ " or coalesce(ppa.id, pa.id) in "
+ " (SELECT allocationId.id FROM ProductStaff WHERE staffId.id = :staffId and type = :resType)"
Дайте нам знать, если это по-прежнему приводит к неверным записям.
это может сработать, но это сделает запрос большим, поскольку в моем запросе уже есть еще кое-что или случай, о котором я здесь не писал. Есть ли другая альтернатива??
@ KJEjava48 Дело в том, какие записи не являются «подходящими» и почему? Есть дубликаты, недостающие записи или другие проблемы? Я могу предложить начать идентифицировать один из этих случаев и проверить, какое из предложенийwhere не соответствует правильно. Вы можете создать базовый тест JUnit, в котором вы постепенно создадите запрос hql и попытаетесь найти проблему, используя этот инкрементный подход.
На самом деле отсутствуют некоторые записи всякий раз, когда возникает второй случай..т.е., когда payeeId = null и AllocationId существует... записи до этой точки не вернутся..
Я хочу COALESCE(payeeId.allocationId.productId.id,locationId.productId.id), но он будет работать, только если это просто COALESCE(payeeId,locationId)
Я обновил ответ, пытаясь построить запрос, который может использовать предложение объединения по вашему желанию.
Вуаля. Обновленный ответ сработал... Это правильный и простой способ сделать это??? Соединение кажется немного неудобным.
Что ж, при выполнении payeeId.allocationId.productId.id вы неявно указываете движку HQL выполнить соединения между Payment и другими таблицами; то, что я сделал, это просто сказал явно и сделал это в режиме левого соединения, это похоже на необязательную переписку. Я обычно полагаюсь на соединения при работе с sql, потому что (ИМХО) — это более линейный способ просмотра отношений между записями таблицы. Возможно, движок HQL все еще не может разбить сложное выражение, такое как payeeId.allocationId.productId.id, в качестве аргумента объединения, поэтому вам придется направить его к «простой» цели.
ок. спасибо за ответ
Можете ли вы предоставить более подробную информацию о других объектах, указанных в запросе или упомянутых выше? Я говорю о GroupLink, GroupStaff, ProductStaff или Recipient and Product. Без них сложно воспроизвести поведение запроса.