|

Для некоторых приложений средств HQL бывает недостаточно и возникает необходимость использовать родной SQL вашей СУБД. Элементарный пример -- вызов хранимой процедуры БД. В Hibernate для исполнения "родных" запросов служит метод createSQLQuery(String queryString) объекта org.hibernate.Session , который возвращает екземпляр org.hibernate.SQLQuery . Напомню, что для запросов на языке HQL используется метод createQuery(String queryString) того же класса, который возвращает екземпляр
org.hibernate.Query . Класс org.hibernate.SQLQuery имеет несколько специфичных для него методов и здесь будут использованы некоторые из них. Во всех примерах екземпляр класса называется . Он может быть получен стандартным способом: new Configuration().configure().buildSessionFactory().getCurrentSession(); Класс здесь называется отображенным, если для него существует , который зарегистрирован в . Класс имеет свойство , если в нем определены публичные методы и (setter и getter) для соответствующего поля property согласно соглашению Sun по наименованию методов класоов: property: setProperty/getProperty proPerty: setProPerty/getProPerty PROPERTY: setPROPERTY/getPROPERTY Для демонстрации примеров используется база данных Oracle, состоящая из двух таблиц, с вязанных внешним ключом. Таблица содержит информацию о группах деревьев ботанического сада. Каждая такая группа имеет уникальный номер, название, сорт (в группу входят деревья одного сорта) и количество деревьев в группе. Таблица содержит информацию о сортах деревьев ботанического сада. Схема базы данных приведена ниже: 
Диаграмма классов, отображенных на эти таблицы выглядит так: 
Связь между классами я здесь не показал — она очевидна. Структура этих классов вместе с методами-акцессорами (getters & setters) в Eclipse: 
Здесь не описана структура конфигурационных файлов (hibernate.cfg.xml, Tree.nbm.xml и Sort.nbm.xml), так как объектно-реляционное отображение довольно тривиально и его легко реализовать при необходимости самостоятельно. Примеры Выборка отображенных объектов одного класса Задача. Необходимо получить список персистентных объектов класса , который отображен в . Решение. Воспользуемся методом для выполнения запроса. Обратите внимание: точка с запятой в конце запроса не ставится! sess.createSQLQuery("SELECT * FROM TREE").addEntity(Tree.class).list(); В данном случае класс должен быть отображен на таблицу . Вызов предписывает рассматривать результат запроса как набор классов . Выборка отображенных объектов разных классов в одном запросе Задача. Получить декартово произведение таблиц и . Решение. session.createSQLQuery("select {tree.*}, {sort.*} from tree, sort").addEntity("tree", Tree.class).addEntity ("sort", Sort.class).list(); Код внутри запроса «подставляет» вместо себя в конечный запрос названия всех свойств отображенного класса, который соотносится с алиасом aliasname. Таким образом, вместо в конечном запросе будет помещен код, эквивалентный следующему: tree.id, tree.name, tree.sort, tree.count А вместо -- Во избежание конфликта имен ( есть в обоих классах) HIBERTNATE всегда подставляет сгенерированные алиасы, которые заканчиваются цифрами и знаками подчеркивания. Именно поэтому код будет не такой же, как тот, что выше, а эквивалентный ему. Отдельно следует описать код который обрабатывает полученный список значений. Этот список содержит объекты типа . Таким образом, результатом запроса будет список одномерных массивов базового типа . Для получения экземпляров классов и нужно сначала привести элемент списка к типу , а потом привести к нужному типу каждый из элементов этого массива. Ниже приведен код, который выводит в стандартный поток результат декартового произведения таблиц и : List list = session.createSQLQuery("select {tree.*}, {sort.*} from tree, sort").addEntity("tree", Tree.class).addEntity ("sort", Sort.class).list();
for (Object listelement : result) { Object[] objectarray = (Object[]) listelement; Tree tree = (Tree) objectarray[0]; Sort sort = (Sort) objectarray[1]; System.out.println("Tree: " + tree + ", sort: " + sort); } Обратите внимание на порядок объектов классов и в — он соответствует порядку следования и в запросе! Выборка отображенных объектов связанных классов Задача. Есть класс , который отображен на таблицу . В классе есть свойство типа (таблицы и связанны внешним ключом). Необходимо получить список персистентных объектов класса вместе с объектами , на которые они ссылаются. Решение. sess.createSQLQuery("SELECT * FROM TREE").addEntity("tree", Tree.class).addJoin("tree.treeSort").list(); Выборка неотображенных объектов Задача. Получить количество деревьев всех сортов. Результат выборки представить в виде POJO, который не отображен в файле конфигурации HIBERNATE. Решение. Создаем класс со свойствами и (свойства должны иметь корректные методы-акцессоры). Результат может быть получен после исполнения кода: session.createSQLQuery("select name sortname, sum(count) countofsort from tree, sort where tree.sort=sort.id group by sortname") .addScalar("sortName", Hibernate.STRING).addScalar("countOfSort", Hibernate.INTEGER).setResultTransformer (Transformers.aliasToBean(SortCount .class)).list(); Метод addScalar(String string, Type type) подсказывает HIBERNATE названия и тип полей результата запроса, а цепочка методов setResultTransformer(Transformers.aliasToBean(Contact.class)).list() “превращает“ его в список объектов класса . Если убрать из кода оба метода , возникнет исключение org.hibernate.PropertyNotFoundException , вызванное отсутствием в классе сеттеров для свойств и . Причина возникновения исключения в том, что большинство СУБД регистронезависимые и передают названия полей результата запроса в составе в верхнем регистре. Как следствие — названия свойств и , а не и . Один из вариантов решения проблемы — создание в классе сеттеров и , а другим — использование методов с явным указанием названий (и типов) полей результата запроса. Второй вариант ликвидирует разногласие между метаданными СУБД и правилом наименования методов в , а также быстрее работает. Вызов хранимой функции базы данных Oracle Задача. В базе данных есть хранимая процедура (функция) , которая принимает id группы деревьев сада в качестве аргумента и в качестве результата возвращает место этой группы среди всех по количеству деревьев в ней. Получить результат работы такой хранимой процедуры базы данных Oracle. Решение. Функция принимает в качестве параметра и возвращает значение целочисленного типа. SQL-запрос для получения результата имеет такой вид: SELECT GETPOSITION(12) FROM DUAL; Точку с запятой нужно опустить, а число 12 заменить на целочисленную переменную, которая содержит некоторой группы деревьев. Также обязательно добавить алиас, иначе СУБД сгенерирует свой (Oracle, например, так и назовет поле — , акцессоры для которого часто просто невозможно создать. Результат запроса можно поместить в любой класс, в котором есть свойство с именем алиаса. Назовем алиас и создадим класс : public class Position { private int position; public int getPosition() { return position; }
public void setPosition(int position) { this.position = position; } } В данном случае результат работы функции можно получить таким образом: int group = 25;
Position position = (Position) session.createSQLQuery("select getposition("+group+") position from dual").addScalar ("position", Hibernate.INTEGER).setResultTransformer(Transformers.aliasToBean(Position.class)).uniqueResult(); Результат будет помещен в переменную . Ничего нового в этом коде нет, разве что метод , который возвращает не список значений, ка метод , а одно значение. Выводы HIBERNATE — довольно гибкий фреймворк, он позволяет многие действия произвести разными способами. Например, запрос на выборку можно осуществить с помощью HQL, Criteria и Native SQL. Тот или иной выбор зависит скорее от личных предпочтений разработчика, но в ряде случаев без использования родного языка запросов СУБД не обойтись. Это справедливо в первую очередь для СУБД с развитыми процедурными расширениями SQL: Oracle, MS SQL Server, IBM DB2. |