Часть вторая
В прошлом номере "Компьютерных вестей" я начал рассказ о такой полезной технологии, как ORM, и о фреймворке Hibernate, который позволяет воспользоваться всеми её преимуществами в Java-приложениях. Сегодня мы продолжим этот рассказ и познакомимся с Hibernate ещё ближе.
Продолжаем конфигурировать
В первой части статьи мы с вами успели поговорить о том, как пишутся конфигурационные XML-файлы для инициализации всего фреймворка, в целом. Однако, к сожалению или к счастью, одними только ими отделаться не удастся - нужно ещё писать конфигурационные файлы для каждого из классов, которые используются в базе данных. Эти файлы нужны для того, чтобы "замапить" классы, значения полей которых хранятся в реляционной базе данных - собственно говоря, именно благодаря этим файлам Hibernate "знает", каким образом записывать и считывать данные. Давайте, как обычно, посмотрим на пример подобного mapping-файла, а затем уже разберёмся, зачем и почему мы написали каждую строчку в нём. Пример конфигурационного файла для записи класса в базе данных вы с лёгкостью найдёте в листинге.
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="events.Person" table="PERSON"> <id name="id" column="PERSON_ID"> <generator class="native"/> </id> <property name="age"/> <property name="firstname"/> <property name="lastname"/> <set name="events" table="PERSON_EVENT"> <key column="PERSON_ID"/> <many-to-many column="EVENT_ID" class="events.Event"/> </set> <set name="emailAddresses" table="PERSON_EMAIL_ADDR"> <key column="PERSON_ID"/> <element type="string" column="EMAIL_ADDR"/> </set> </class> </hibernate-mapping>
Первые две строки, знакомые каждому, кто встречался с XML-файлами, - конструкции, которые просто обозначают принадлежность данного файла к определённой категории. hibernate-mapping - это корневой элемент всего mapping-файла, внутрь которого вложены элементы-class'ы. Именно вокруг этих элементов всё и крутится - как вы, наверное, уже догадались, каждый из таких элементов соответствует какому-либо классу, который "мапится" в базу данных.
Как видите, у самого элемента class есть два параметра. Name - это, естественно, имя класса, который будет "мапиться", а table - это та самая таблица, которой будет поручено хранить в себе информацию об экземплярах данного класса. Внутренние элементы этого элемента - это поля той самой таблицы, в которой Hibernate (или, вернее, реляционная СУБД, к которой мы будем обращаться через этот фреймворк) будет хранить информацию о наших объектах. Конечно, в каждой таблице должно быть играющее роль ключа автоинкрементное поле id, которое у нас и идёт в списке первым. Генератор для этого поля выставлен native - так будет меньше проблем в нашем простом случае, однако если у вас случай более сложный, вам может потребоваться создать и свой собственный генератор. Далее следуют описания других полей, реализуемые с помощью элементов property - каждому из этих элементов соответствует какое-то одно из полей в той таблице, о которой мы с вами сейчас ведём разговор.
Ровные ряды элементов property нарушает элемент set. Их, как видите, в нашем конфигурационном файле два. Эти элементы описывают поля, связанные с другими полями таблицы, и могут быть разных видов. В нашем с вами примере первый set описан как many-to-many - то есть, каждому из элементов нашей таблицы может соответствовать несколько элементов из таблицы PERSON_EVENT, и наоборот. При этом определение того, какой из записей нашей таблицы соответствует запись в таблице PERSON_EVENT, происходит по полю PERSON_ID второй таблицы. Второй set в нашем с вами конфигурационном файле - это уже более простой тип отношений, то есть, как вы сами понимаете, каждый e-mail может относиться только к одному человеку, что и определяет специфику записи их в базу данных.
Конфигурационным файлам, в которых записывается информация о том, каким именно образом будет храниться информация об экземплярах данного класса в базе данных, рекомендуется давать имена вида class-name.hbm.xml, где class-name - это имя того самого класса, который и описывается в данном конкретном конфигурационном файле. В общем-то, никакой необходимости следовать этому негласному правилу нет, поскольку, как вы уже знаете, все имена mapping-файлов записываются в общий конфигурационный файл Hibernate, который мы с вами успели разобрать в предыдущей части статьи. Однако, думается, использование именно таких имён будет просто хорошим тоном в отношении своих коллег по проекту.
Собственно, программный код
Мы с вами уже немало поговорили о том, как писать конфигурационные файлы. Пора бы уже узнать, какой именно программный код поможет нам загрузить данные из базы и работать с ними. Что ж, давайте посмотрим на этот код - для начала на тот код, который загружает конфигурацию фреймворка и инициализирует его. Этот код приведён в листинге.
try { sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); }
Блок try...catch здесь, в общем-то, не сказать чтобы так уж необходим - но, скажем так, весьма и весьма полезен, поскольку на этапе инициализации может произойти множество различных вещей, которые лучше перехватывать с помощью этого блока. Переменную sessionFactory лучше сделать статическим полем какого-нибудь специально выделенного под это дело класса; также статическим стоит сделать и метод инициализации, код которого и приведён в листинге. Впрочем, конечно, не обязательно делать sessionFactory полем, которое видно всем остальным классам в программе - гораздо лучше сделать метод-обёртку, возвращающий sessionFactory, и само поле сделать приватным.
Что ж, если у нас теперь есть sessionFactory, мы можем с помощью этого класса создавать и, собственно, сами сессии, и затем уже в рамках них работать с экземплярами классов, которые мы недавно прописывали в конфигурационных файлах. Давайте посмотрим на Hibernate в действии - каким образом нужно работать с сессиями. Взгляните на листинг, а потом мы обсудим, что же именно делает тот код, который написан в нём.
Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Person thePerson = new Person(); thePerson.setFirstname(firstname); thePerson.setLastname(lastname); session.save(thePerson); session.getTransaction().commit();
Итак, пойдём по строчкам. Первая строка - это получение сессии от sessionFactory - экземпляр Hibernate'овского класса "фабрики сессий", который мы с вами создавали в предыдущем листинге. После этого мы начинаем транзакцию с помощью полученной от фабрики сессии - всё-таки мы работаем с базой данных, и транзакции никто не отменял. В третьей по счёту строчке мы создаём экземпляр того самого класса, который у нас был расписан в mapping-файле. Следующие две строчки посвящены изменению значений внутренних полей этого класса с помощью специальных методов-обёрток, что в данном примере, в общем-то, несущественно, поскольку мы ведём разговор о Hibernate. Зато следующая строчка уже существенна - с её помощью мы добавляем новый объект в таблицу (как видно из названия метода, сохраняем его в ней). Ну а последней строчкой мы подтверждаем транзакцию, и фреймворк Hibernate вносит все необходимые изменения в базу данных.
Как видите, ничего особенно сложного в добавлении новых объектов в базу нет. Впрочем, нет никаких сложностей и в том, чтобы извлекать объекты из базы - Hibernate предоставляет программисту весьма гибкий и удобный механизм, который частично позволяет, частично заставляет использовать SQL - но, конечно, далеко не в тех объёмах, в каких это надо было бы без Hibernate. Например, получить с помощью сессий в Hibernate данные об экземпляре того или иного класса, хранящиеся в таблице, можно следующей строкой кода: Person aPerson = (Person) session.createQuery("select p from Person p left join fetch p.events where p.id = :pid").setParameter("pid", personId).uniqueResult();. Здесь мы, как видите, используем join-запрос для объединения данных из двух разных таблиц.
Совершенно аналогичным образом записи в базе данных можно через Hibernate обновлять и удалять. Для этого существуют методы update и delete. Впрочем, эти методы совершенно аналогичны методу save, а потому мы их подробно рассматривать не будем - всё-таки объём газетной статьи накладывает весьма существенные ограничения на количество информации, которое можно в ней рассмотреть.
Что осталось за кадром?
Конечно, Hibernate - фреймворк достаточно сложный и комплексный, а потому мы сумели затронуть лишь надводную часть айсберга всех тем знаний, которые нужны для полноценной работы с ним. И, тем не менее, эта часть уже вполне достаточна для того, чтобы, "покрутив" немного фреймворк самостоятельно, приступить к его дальнейшему изучению по более серьёзным источникам.
В общем-то, документации и литературы по Hibernate предостаточно, да и сам он снабжён множеством отличных примеров, замечательно подходящих для быстрого старта в этом фреймворке. Однако есть некоторый минус: дело в том, что почти вся литература по Hibernate англоязычная. Если вы хорошо знаете английский, то, конечно, для вас это совсем не будет помехой. Если же вы английского не знаете... Что ж, вам, полагаю, стоит его выучить, потому что в наши дни без английского в сфере информационных технологий попросту невозможно быть хоть сколь-нибудь компетентным специалистом. Впрочем, если вы всё же решительно настроены изучать Hibernate исключительно по русскоязычным источникам, то могу вас обрадовать: таковые имеются. По крайней мере, один - книга Анила Хемраджани "Гибкая разработка приложений на Java с помощью Spring, Hibernate и Eclipse". В ней рассказывается не только о Hibernate, но и о Spring - другом фреймворке для Java, ничуть не менее полезном в современных реалиях промышленного программирования. К сожалению, книгу эту целиком удачной назвать сложно, а потому всё-таки гораздо лучше изучать Hibernate по англоязычным источникам. Только, опять-таки, лучше выбирать те из них, которые написаны не индусами.
В любом случае, успехов вам с Hibernate. Надеюсь, вы не пожалеете времени на изучение этого фреймворка.
Вадим СТАНКЕВИЧ,
dreamdrusch@tut.by
Горячие темы