Ответ 1
Я выяснил решение через несколько часов подряд. Надеюсь, это полезно для других. Было три основных момента, которые мне нужно было решить, чтобы сделать это возможным:
- Добавить проекцию
- Создайте правильные соединения
- Правильно сопоставьте подзапрос с основными критериями
Я выделил каждый из них в приведенном ниже коде.
Во-первых, чтобы избавиться от исключения, я обнаружил, что в подзапросе нужна проекция, выделенная ниже. Я просто сделал проекцию на свойство "id" экземпляра.
Во-вторых, чтобы получить соединение, я использовал методы Criteria.createCriteria() для создания левого внешнего соединения. Поскольку у меня было несколько условий на разных уровнях соединения, мне пришлось сохранить объединенные критерии и приложить выражения к ним отдельно. Это позволяет мне делать свое выражение OR в подзапросе.
Наконец, мне пришлось добавить предложение eqProperty() для сопоставления подзапроса с основными критериями. Так же, как это должно быть в результате SQL, я использовал: instance.id = i.id. Поскольку я уже сопоставил критерий экземпляра с "i" и добавлял это предложение к критерию ценности, это переводилось в SQL: v.instance_id = i.id.
Здесь рабочий код:
public List<Instance> getMatchingInstances(Map<String, String> attrValues) {
Criteria crit = session.createCriteria(Instance.class, "i");
for(Map.Entry<String, String> entry : attrValues) {
String attrName = entry.getKey();
String val = entry.getValue();
// Create the subquery
DetachedCriteria valueCrit = DetachedCriteria.forClass(Value.class, "v");
// Join the Attribute object (left outer join)
DetachedCriteria attrCrit =
valueCrit.createCriteria("attribute", CriteriaSpecification.LEFT_JOIN);
// Put together the OR statement on the Attribute joined criterion.
Criterion localAttr = Restrictions.eq("v.localAttributeName", attrName);
Criterion globalAttr = Restrictions.eq("name", attrName);
attrCrit.add(Restrictions.or(localAttr, globalAttr));
// Simple column equality on the subquery criterion.
valueCrit.add(Restrictions.eq("value", val));
// Map the subquery back to the outer query.
valueCrit.add(Restrictions.eqProperty("instance.id", "i.id"));
// Add the missing projection.
valueCrit.setProjection(Projections.property("id"));
// Add this subquery to the outer query.
crit.add(Subqueries.exists(valueCrit));
}
return crit.list();
}