barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

пятница, 6 июля 2012 г.

Hibernate Criteria API: баг при одновременном использовании criteria.createAlias и criteria.setProjection(Projections.rowCount())

Мда, багов в Hibernate хватает. Наткнулся на ещё одну неприятность (это правда возможно и не совсем баг, просто немного неожиданное поведение) при попытке посчитать число строк с помощью Criteria, использующего псевдонимы (заданные с помощью criteria.createAlias). Например, пусть есть 3 таблицы: t1, t2 и t3. Соответствующие им POJO-классы пусть называются также. Связаны они связями много-к-одному через внешние ключи: t1.t2fk = t2.id, t2.t3fk = t3.id. Пусть есть некоторая Criteria, которая выбирает объекты t1, исходя из условия, накладываемого на связанные с ними объект t3. Чтобы в Criteria задать ограничение на свойство объекта t3, для соответствующей ассоциации с помощью criteria.createAlias создан псевдоним:
criteria.createAlias("t2fk.t3fk","t3alias")
Без проекции она генерирует SQL с двумя JOIN`ами:
select ... from t1 inner join t2 on ... inner join t3 t3alias_ on ... where t3alias_.id=?
При добавлении проекции Projections.rowCount() можно ожидать запрос вроде:
select count(*) from t1 inner join t2 on ... inner join t3 t3alias_ on ... where  t3alias_ .id=?
Однако, Hibernate поступает более кардинально, убирая все объединения из запроса:
select count(*) from t1 where t3alias_ .id=?
Естественно, запрос становится невиладным, причем Hibernate сам это понимает ещё до его выполнения:
ERROR: missing FROM-clause entry for table "t3alias_"
Решается проблема следующим образом: при использовании проекции необходимо задавать псевдоним не только для конечной таблицы t3, по которой выполняется отбор строк, но и для промежуточной таблицы t2 (а в общем случае - для всех таблиц, лежащих на пути между корневой таблицей и конечной таблицей, которая используется в where):
criteria.createAlias("t2fk","t2alias")
criteria.createAlias("t2alias.t3fk","t3alias");
PS Спасибо за подсказку http://facingtech.blogspot.com/2010/09/applying-projection-to-hibernate.html. Видимо, данное поведение разработчики Hibernate считают корректным и исправлять не собираются, т.к. соответствующий тикет висит ещё с версии 3.20: https://hibernate.onjira.com/browse/HHH-5719.

Комментариев нет:

Отправить комментарий