Что такое hibernate в java
Перейти к содержимому

Что такое hibernate в java

  • автор:

Что такое Hibernate в Java?

Hibernate — это фреймворк для объектно-реляционного отображения (ORM) в языке программирования Java. Он обеспечивает удобные средства взаимодействия с базами данных, представляя данные в виде объектов, что позволяет разработчикам работать с базами данных, используя объектно-ориентированный подход.

Основные концепции Hibernate:

  1. Сущности (Entities):
    • Сущности представляют собой объекты, которые отображают таблицы в базе данных. Каждая сущность обычно соответствует отдельной таблице в базе данных.
  2. Отношения между сущностями:
    • Hibernate поддерживает различные типы отношений между сущностями, такие как один к одному, один ко многим и многие ко многим.
  3. Аннотации:
    • Аннотации используются для маппинга Java-классов на таблицы базы данных. Например, аннотация @Entity указывает, что класс является сущностью.
  4. Сессии (Sessions):
    • Сессии Hibernate представляют собой механизм взаимодействия с базой данных. Сессия управляет жизненным циклом сущности и выполняет операции базы данных.
  5. HQL (Hibernate Query Language):
    • HQL представляет собой объектно-ориентированный язык запросов, похожий на SQL, но использующий сущности Java вместо таблиц базы данных.
  6. Каскадирование (Cascading):
    • Каскадирование позволяет автоматически распространять операции (например, сохранение, обновление, удаление) сущностей на связанные с ними сущности.

Hibernate в вопросах и ответах

Hibernate — это библиотека с открытым исходным кодом (open source) для Java, предназначенная для решения задач ORM (object-relational mapping, объектно-реляционного отображения). Она представляет собой свободное программное обеспечение, распространяемое на условиях GNU Lesser General Public License. Hibernate Framework имеет легкий в использовании каркас для отображения объектно-ориентированной модели данных в традиционные реляционные базы данных и предоставляет стандартные средства JPA.

2. Преимущества использования Hibernate Framework?

Библиотека Hibernate является одним из самых востребованных ORM фреймворков для Java, поскольку :

  • позволяет разработчику сосредоточиться на бизнес логике, не отвлекаясь на управление ресурсами;
  • предоставляет собственный язык запросов (HQL), внешне похожий на SQL. Необходимо отметить, что HQL полностью объектно-ориентирован и понимает такие принципы, как наследование, полиморфизм и ассоциации (связи);
  • может использовать также чистый SQL, а, следовательно, поддерживает возможность оптимизации запросов и работы с любым сторонним провайдером БД;
  • поддерживает JPA аннотации, что позволяет сделать реализацию кода независимой;
  • поддерживает разные уровни cache, а следовательно может повысить производительность;
  • поддерживает ленивую инициализацию используя proxy объекты и выполняя запросы к базе данных только по необходимости;
  • интегрируется с другими Java EE фреймворками; например, Spring Framework поддерживает встроенную интеграцию с Hibernate;
  • является широко распространенным open source продуктом. Благодаря этому доступны тысячи открытых статей, примеров, а также документация по использованию фреймворка.

3. Объекты Hibernate SessionFactory, Session и Transaction

SessionFactory Экземпляр SessionFactory создается методом buildSessionFactory (ServiceRegistry) объекта org.hibernate.Configuration и предназначен для получения объекта Session. Инициализируется SessionFactory один раз. Внутреннее состояние SessionFactory неизменно (immutable), т.е. он является потокобезопасным. Internal state (внутреннее состояние) включает в себя все метаданные об Object Relational Mapping, определяемые при создании SessionFactory.
SessionFactory также предоставляет методы для получения метаданных класса и статистики, типа данных о втором уровне кэша, выполняемых запросах и т.д.
Session Однопоточный объект, устанавливающий связь между объектами/сущностями приложения и базой данных. Сессия создается при необходимости работы с БД и ее необходимо закрыть сразу же после использования. Экземпляр Session является интерфейсом между кодом в java приложении и hibernate framework, предоставляя методы для операций CRUD.
Transaction Однопоточный объект, используемый для атомарных операций. Это абстракция приложения от основных JDBC или JTA транзакций. org.hibernate.Session может занимать несколько org.hibernate.Transaction в определенных случаях.

Пример использования объектов SessionFactory, Session, Transaction.

4. Конфигурационный файл Hibernate

Файл конфигурации hibernate.cfg.xml содержит информацию о базе данных (драйвер, пул подключений, диалект) и параметрах подключения к серверу БД (url, login, password). В качестве параметров подключения можно использовать как JDBC, так и JNDI. В файле конфигурации также определяются дополнительные параметры, которые будут использованы при работе с сервером БД, Так, здесь необходимо определить маппинги сущностей/классов.

Чтобы отобразить в консоли SQL-скрипты, генерируемые Hibernate, необходимо в hibernate.cfg.xml определить истиное значение свойства «show_sql». Помните, что это необходимо использовать только на уровне разработки и тестирования. В финальной версии свойство «show_sql» должно быть отключено.

Пример файла конфигурации связанных сущностей.

5. Файл mapping

Файл отображения (mapping file) используется для связи entity бинов с таблицами базы данных. Содержимое файла имеет формат XML. Файл mapping можно использовать вместо аннотаций JPA. Особенно он становится необходимым при использовании сторонних библиотек.

6. Важные аннотации для отображения в Hibernate

Наиболее употребительные аннотации Hibernate из пакета javax.persistence представлены в следующей таблице :

@Entity Определение класса как сущность entity bean
@Table, @Column Определение таблицы в БД и наименования колонки в таблице
@Id Поле Primary Key в сущности entity bean
@GeneratedValue Определение стратегии создания основных ключей
@SequenceGenerator Определение генератора последовательности
@OneToMany, @ManyToOne, @ManyToMany Определение связи между сущностными бинами

Подробнее об аннотациях в сущностных бинах.

7. Отличие методов openSession и getCurrentSession

Методы openSession и getCurrentSession объекта SessionFactory возвращают сессию Session.

Метод getCurrentSession объекта SessionFactory возвращает сессию, связанную с контекстом. Но для того, чтобы метод вернул не NULL, необходимо настроить параметр current_session_context_class в конфигурационном файле hibernate. Поскольку полученный объект Session связан с контекстом hibernate, то отпадает необходимость в его закрытии; он закрывается вместе с закрытием SessionFactory.

 thread 

Метод openSession объекта SessionFactory всегда создает новую сессию. В этом случае необходимо обязательно контролировать закрытие объекта сессии по завершению всех операций с базой данных. Для многопоточной среды необходимо создавать новый объект Session для каждого запроса.

При загрузке больших объемов данных без удержания большого количества информации в кэше можно использовать метод openStatelessSession(), который возвращает Session без поддержки состояния. Полученный объект не реализует первый уровень кэширования и не взаимодействует со вторым уровнем. Сюда же можно отнести игнорирование коллекций и некоторых обработчиков событий.

8. Отличие методов get и load объекта Session

Для загрузки информации из базы данных в виде набора/коллекции сущностей объект Session имеет несколько методов. Наиболее часто используемые методы get и load. Метод get загружает данные сразу же при вызове, в то время как load использует прокси объект и загружает данные только тогда, когда это требуется на самом деле (при обращении к данным). В этом плане load имеет преимущество в плане ленивой загрузки данных.

Метод load вызывает исключение, если данные не найдены. Поэтому load нужно использовать только при уверенности в существовании данных. Если необходимо удостовериться в наличии данных в БД, то нужно использовать метод get.

9. Различные состояния Entity Bean

Сущность Entity Bean может находиться в одном из трех состояний :

transient Состояние сущности, при котором она не была связана с какой-либо сессией и не является persistent. Сущность может перейти в состояние persistent при вызове метода save(), persist() или saveOrUpdate() объекта сессии.
persistent Экземпляр сущности, полученный методами get() или load() объекта сессии, находится в состоянии persistent, т.е. связан с сессией. Из состояния persistent сущность можно перевести в transient после вызова метода delete() сессии.
detached Если объект находился в сотоянии persistent, но перестал быть связанным с какой-либо сессией, то он переходит в состояние detached. Такой объект можно сделать персистентным, используя методы update(), saveOrUpdate(), lock() или replicate().

Из состояний transient и detached объект можно перевести в состояние persistent в виде нового объекта после вызова метода merge().

10. Отличия методов save, saveOrUpdate и persist

Метод save используется для сохранения сущности в базе данных. Этот метод возвращает сгенерированный идентификатор. Возникаемые проблемы с использованием save связаны с тем, что метод может быть вызван без транзакции. А следовательно если имеется отображение нескольких связанных объектов, то только первичный объект будет сохранен, т.е. можно получить несогласованные данные.

Метод hibernate persist аналогичен save, но выполняется с транзакцией. Метод persist не возвращает сгенерированный идентификатор сразу.

Метод saveOrUpdate используется для вставки или обновления сущности. Если объект уже присутствуют в базе данных, то будет выполнен запрос обновления. Метод saveOrUpdate можно применять без транзакции, но это может привести к аналогичным проблемам, как и в случае с методом save.

11. Использование метода сессии merge

Метод Hibernate merge объекта сессии может быть использован для обновления существующих значений. Необходимо помнить, что данный метод создает и возвращает копию из переданного объекта сущности. Возвращаемый объект является частью контекста персистентности с отслеживанием любых изменений, а переданный объект не отслеживается.

12. Отсутствие в Entity Bean конструктора без параметров

Hibernate использует рефлексию для создания экземпляров Entity бинов при вызове методов get или load. Для этого используется метод Class.newInstance, который требует наличия конструктора без параметров. Поэтому, в случае его отсутствия, будет вызвано исключение HibernateException.

13. Entity Bean не должна быть final

Hibernate использует прокси классы для ленивой (lazy) загрузки данных (т.е. не сразу, а по необходимости). Это достигается с помощью расширения Entity Bean. Отсюда следует, что если бы он был final, то это было бы невозможно.

Ленивая загрузка данных во многих случаях повышает производительность, а следовательно важна и от нее не следует отказываться.

14. Сортировка данных в Hibernate

При использовании алгоритмов сортировки из Collection API используется сортированный список (sorted list). Для маленьких коллекций это не приводит к излишнему расходу ресурсов. Однако на больших коллекциях это может привести к потере производительности и ошибкам OutOfMemory.

Entity Bean’ы для работы с сортированными коллекциями должны реализовывать интерфейс Comparable/Comparator. При использовании фреймворка Hibernate для загрузки данных можно применить Collection API и команду order by для получения сортированного списка (ordered list). Ordered list является лучшим способом получения sorted list, т.к. используется сортировка на уровне базы данных, работающая быстрее и не приводящая к утечке памяти. Пример запроса к БД для получения ordered list :

List list = session.createCriteria(Employee.class) .addOrder(Order.desc("id")).list();

Hibernate использует следующие типы коллекций : Bag, Set, List, Array, Map.

15. Использование Query Cache в Hibernate

Hibernate реализует область кэша для запросов ResultSet, который тесно взаимодействует с кэшем второго уровня Hibernate. Для подключения этой дополнительной функции необходимо определить истинное значение свойства hibernate.cache.use_query_cache в файле конфигурации hibernate.cfg.xml и в коде при обращении к БД использовать метод setCacheable(true). Кэшированные запросы полезны только при их частом исполнении с повторяющимися параметрами.

Определение свойства в файле конфигурации Hibernate :

 true 

Формирование запроса с использованием метода setCacheable (true) :

Query query = session.createQuery("from Employee"); query.setCacheable(true); query.setCacheRegion("ALL_EMP");

16. Язык запросов HQL

Hibernate включает мощный язык запросов HQL (Hibernate Query Language), который очень похож на родной SQL. В сравнении с SQL, HQL полностью объектно-ориентирован и использует понятия наследования, полиформизма и связывания.

HQL использует имя класса взамен имени таблицы, а также имя свойства вместо имени колонки. Пример HQL :

Query query = session.createQuery("from ContactEntity where firstName = :paramName"); query.setParameter("paramName", "Nick"); List list = query.list();

17. Нативный SQL-запрос в Hibernate

Для выполнения нативного запроса необходимо использовать SQLQuery, который может выполнять чистый SQL-запрос. Но необходимо учитывать, что в этом случае можно потерять все преимущества HQL (ассоциации, кэширование). Пример нативного SQL-запроса :

String sql ; Query query; sql = «select id, name, salary from employees»; query = session.createSQLQuery(sql).addEntity(Employee.class); List list = query.list(); for (Iterator it = users.iterator(); it.hasNext();)

Обратите внимание, что при формировании Query был добавлен класс Employee.class, в результате чего метод list() объекта Query вернул коллекцию сотрудников List.

В следующем коде при формировании Query нет привязки к конкретному классу. В результате метод list() возвращает коллекцию объектов List.

query = session.createSQLQuery(sql); List rows = query.list(); for(Object[] row : rows)

18. Преимущества поддержки нативного SQL-запроса

Использование нативного SQL может быть необходимо при выполнении некоторых запросов к базам данных, которые могут не поддерживаться в Hibernate. Т.е. включение в запросы специфичных для БД «фишек».

19. Именованный запрос, Named SQL Query

Hibernate поддерживает использование именованных запросов, которые можно определить в одном месте и использовать в любом месте в коде. Именованные запросы поддерживают как HQL, так и Native SQL. Для создания Named SQL Query можно использовать JPA аннотации @NamedQuery, @NamedNativeQuery или конфигурационный файл отображения (mapping files). Пример описания и использования Named SQL Query.

// Определение Named SQL Query @NamedQuery( name = "getContacts", query = "select ce from ContactEntity ce where ce.id >=:id" ) // Сущность @Entity @Table(name="contact", schema = "", catalog = "javastudy") public class ContactEntity < >. . . // использование Named SQL Query Query query = session.getNamedQuery("getContacts") .setString("id", "10");

20. Преимущества именованных запросов Named SQL Query

Named Query имеют глобальный характер, т.е. заданные в одном месте, доступны в любом месте кода. Синтаксис Named Query проверяется при создании SessionFactory, что позволяет заметить ошибку на раннем этапе, а не при запущенном приложении и выполнении запроса.

Одним из основных недостатков именованного запроса является то, что его сложнее отлаживать. Сложности могут быть связаны с поиском места определения запроса. Поэтому не разбрасывайтесь описанием запросов в различных участках. Можно все связанные с сущностью запросы описать непосредственно в классе, используя аннотацию @NamedQueries, как это показано в следующем коде :

@Entity @Table(name=»student») @NamedQueries(< @NamedQuery(name="selectStudents", query="select st from Student st"), @NamedQuery(name="updateStudentRecord", query="update Student st set st.sname =: name where st.id =:id") >) public class Student

21. Использование org.hibernate.Criteria

Hibernate Criteria API представляет альтернативный подход HQL и позволяет выполнять запросы в БД без написания SQL кода. Для создания экземпляров Criteria используется класс Session. Пример Criteria с необязательным обрамлением в транзакцию :

session.beginTransaction(); List users users = session.createCriteria(User.class) .setMaxResults(10).list(); session.getTransaction().commit();

Приведенный выше запрос вернет первые 10 записей из таблицы сущности User. Метод setMaxResults представляет собой аналог команды LIMIT в SQL-запросе. Чтобы прочитать определенное количество записей с с определенной позиции (LIMIT 2, 15) необходимо дополнительно использовать метод setFirstResult :

List users users = session.createCriteria(User.class) .setFirstResult(2) .setMaxResults(15).list();

Подробнее о org.hibernate.Criteria можно прочитать здесь.

22. Hibernate proxy и ленивая загрузка (lazy load)

Hibernate может использовать прокси для поддержки отложенной загрузки. При соответствующем атрибут fetch аннотации связи (fetch определяет стратегию загрузки дочерних объектов) из базы данных не загружаются связанные объекты. При первом обращении к дочернему объекту с помощью метода get, если связанная сущность отсутствует в кэше сессии, то прокси код перейдет к базе данных для загрузки связанной сущности. Для этого используется javassist, чтобы эффективно и динамически создавать реализации подклассов Entity Bean объектов.

Подробнее об атрибуте загрузки связанных объектов fetch.

23. Каскадные связи

При наличии зависимостей (связей) между сущностями необходимо определить влияние различных операций одной сущности на связанные. Это можно реализовать с помощью аннотации каскадных связей @Cascade. Пример использования @Cascade :

import org.hibernate.annotations.Cascade; @Entity @Table(name = «EMPLOYEE») public class Employee

Помните, что имеются некоторые различия между enum CascadeType в Hibernate и в JPA. Поэтому обращайте внимание на импортируемый пакет при использовании аннотации и константы типа. Наиболее часто используемые CascadeType перечисления описаны ниже :

  • None : без каскадирования, т.е. никакая операция для родителя не будет иметь эффекта для ребенка;
  • ALL : все операции родителя будут отражены на ребенке (save, delete, update, evict, lock, replicate, merge, persist);
  • SAVE_UPDATE : операции save и update, доступно только для hibernate;
  • DELETE : в Hibernate передается действие native DELETE;
  • DETATCH, MERGE, PERSIST, REFRESH, REMOVE – для простых операций;
  • LOCK : передает в Hibernate native LOCK действие;
  • REPLICATE : передает в Hibernate native REPLICATE действие.

24. Управление транзакциями

Hibernate не допускает большинство операций без использования транзакций. Для начала транзакции необходимо выполнить метод beginTransaction объекта сессии Session, возвращающий ссылку, которую можно использовать для подтверждения или отката транзакции.

Любое вызываемое в Hibernate исключение автоматически вызывает rollback.

Использование JNDI DataSource в Hibernate

При использовании Hibernate в WEB-приложении и определении соответствующих настроек в контейнере сервлетов, касающихся подключения Datasource, можно использовать JNDI. Для этого необходимо в файле конфигурации hibernate.cfg.xml определить свойство hibernate.connection.datasource :

 java:comp/env/jdbc/MyLocalDB 

Руководство по Hibernate. Архитектура.

Hibernate – это ORM фреймворк для Java с открытым исходным кодом. Эта технология является крайне мощной и имеет высокие показатели производительности.

Hibernate создаёт связь между таблицами в базе данных (далее – БД) и Java-классами и наоборот. Это избавляет разработчиков от огромного количества лишней, рутинной работы, в которой крайне легко допустить ошибку и крайне трудно потом её найти.

Схематично это можно изобразить следующим образом:

hibernate

Какие же преимущества даёт нам использование Hibernate?

  • Обеспечивает простой API для записи и получения Java-объектов в/из БД.
  • Минимизирует доступ к БД, используя стратегии fetching.
  • Не требует сервера приложения.
  • Позволяет нам не работать с типами данных языка SQL, а иметь дело с привычными нам типами данных Java.
  • Заботится о создании связей между Java-классами и таблицами БД с помощью XML-файлов не внося изменения в программный код.
  • Если нам необходимо изменить БД, то достаточно лишь внести изменения в XML-файлы.

Hibernate поддерживает все основные СУБД: MySQL, Oracle, PostgreSQL, Microsoft SQL Server Database, HSQL, DB2.
Hibernate также может работать в связке с такими технологиями, как Maven и J2EE.

Архитектура

Приложение, которе использует Hibernate (в крайне поверхностном представлении) имеет такую архитектуру:

arch

Если мы рассмотрим строение самого Hibernate более подробно, что этот же рисунок будет выглядеть следующим образом:

hibArchDetailed

Hibernate поддерживает такие API, как JDBC, JNDI, JTA.
JDBC обеспечивает простейший уровень абстракции функциональности для реляционных БД. JTA и JNDI, в свою очередь, позволяют Hibernate использовать серверы приложений J2EE.

Давайте рассмотрим отдельно каждый из элементов Hibernate, которые указаны в схеме:

Transaction

Этот объект представляет собой рабочую единицу работы с БД. В Hibernate транзакции обрабатываются менеджером транзакций.

SessionFactory

Самый важный и самый тяжёлый объект (обычно создаётся в единственном экземпляре, при запуске приложения). Нам необходима как минимум одна SessionFactory для каждой БД, каждый из которых конфигурируется отдельным конфигурационным файлом.

Session

Сессия используется для получения физического соединения с БД. Обычно, сессия создаётся при необходимости, а после этого закрывается. Это связано с тем, что эти объекты крайне легковесны. Чтобы понять, что это такое, модно сказать, что создание, чтение, изменение и удаление объектов происходит через объект Session.

Query

Этот объект использует HQL или SQL для чтения/записи данных из/в БД. Экземпляр запроса используется для связывания параметров запроса, ограничения количества результатов, которые будут возвращены и для выполнения запроса.

Configuration

Этот объект используется для создания объекта SessionFactory и конфигурирует сам Hibernate с помощью конфигурационного XML-файла, который объясняет, как обрабатывать объект Session.

Criteria

Используется для создания и выполнения объекто-ориентированного запроса для получения объектов.

Основы Hibernate

Хочу начать со слов благодарности тому человеку, который мне вчера накинул кармы, позволив этим писать мне в персональный блог.

Долго думал, о чем же написать свой «первый» топик… Слово первый не зря взял в кавычки, так как первый топик на самом деле уже был, опыт был к сожалению неудачный — дело закончилось баном.  Решил больше не копипастить. Уверенности тому, что надо написать что-то свое, придал вот этот топик. Решил твердо — пусть это будет и редко, но буду писать сам.

Совсем недавно, по роду свой деятельности, мне пришлось столкнуться с таким понятием как ORM — (англ. Object-relational mapping). В двух словах ORM — это отображение объектов какого-либо объектно-ориентированного языка в структуры реляционных баз данных. Именно объектов, таких, какие они есть, со всеми полями, значениями, отношениями м/у друг другом.

ORM-решением для языка Java, является технология Hibernate, которая не только заботится о связи Java классов с таблицами базы данных (и типов данных Java в типы данных SQL), но также предоставляет средства для автоматического построения запросов и извлечения данных и может значительно уменьшить время разработки, которое обычно тратится на ручное написание SQL и JDBC кода. Hibernate генерирует SQL вызовы и освобождает разработчика от ручной обработки результирующего набора данных и конвертации объектов, сохраняя приложение портируемым во все SQL базы данных.

Итак, перед нами стоит задача написать небольшое приложение, которое бы осуществляло простое взаимодействие с базой данных, посредством технологии Hibernate.

Немного подумав, решил написать так называемый «Виртуальный автопарк». Суть парка такова: есть автобусы, есть маршруты и есть водители. Автобусы и маршруты связаны отношением один ко многим, т.е. на одном маршруте может кататься сразу несколько автобусов. Водители и автобусы связаны отношением многие ко многим, т.е. один водитель может водить разные автобусы и один автобус могут водить разные водители. Вроде ничего сложного.
Вот схема базы данных.

За качество не ругайте — под рукой не оказалось нормального инструмента таблички рисовать…
Вот ссылка на дамп, снятый с базы, вдруг кто-то решит все это дело поднять 🙂
Приступаем к коду. Во первых нам необходимо описать классы наших сущностей, т.е. класс автобуса, водителя и маршрута.
Класс автобус.

import java.util.Set;
import java.util.HashSet;

public class Bus private Long id;
private String number;
private Set drivers = new HashSet();
private Long route_id;

public Bus() >
public void setId(Long id) this .id = id;
>
public void setNumber( String number) this .number = number;
>
public void setDrivers(Set drivers) this .drivers = drivers;
>
public void setRoute_id(Long route_id) this .route_id = route_id;
>
public Long getId() return id;
>
public String getNumber() return number;
>
public Set getDrivers() return drivers;
>
public Long getRoute_id() return route_id;
>
> * This source code was highlighted with Source Code Highlighter .

Класс водитель.

import java.util.Set;
import java.util.HashSet;

public class Driver private Long id;
private String name;
private String surname;
private int age;
private Set busses = new HashSet();

public Driver() >
public void setBusses(Set busses) this .busses = busses;
>
public Set getBusses() return busses;
>
public void setId(Long id) this .id = id;
>
public void setName( String name) this .name = name;
>
public void setSurname( String surname) this .surname = surname;
>
public void setAge( int age) this .age = age;
>
public Long getId() return id;
>
public String getName() return name;
>
public String getSurname() return surname;
>
public int getAge() return age;
>
> * This source code was highlighted with Source Code Highlighter .

И класс маршрут.

import java.util.Set;
import java.util.HashSet;

public class Route private Long id;
private String name;
private int number;
private Set busses = new HashSet();

public Route() >
public void setId(Long id) this .id = id;
>
public void setName( String name) this .name = name;
>
public void setNumber( int number) this .number = number;
>
public void setBusses(Set busses) this .busses = busses;
>
public Long getId() return id;
>
public String getName() return name;
>
public int getNumber() return number;
>
public Set getBusses() return busses;
>
> * This source code was highlighted with Source Code Highlighter .

Заметьте, что все классы сущностей должны соответствовать Java naming conventions, т.е. у них должны быть обязательно геттеры, сеттеры и конструктор по умолчанию. Ничего сложного 🙂

Теперь для наших классов необходимо описать маппинг в виде xml-файлов, эти файлы как раз и будут отвечать за взаимодействие наших объектов с Hibernate и с базой данных.
Bus.hbm.xml

Driver.hbm.xml

Route.hbm.xml

  • Тег hibernate-mapping я думаю понятен, тут ничего говорить не стоит.
  • Тег class имеет два параметра: параметр name — Имя класса (необходимо указывать полный путь с учетом структуры пакетов) и параметр table — имя таблицы в базе данных, на которую будет маппиться наш класс.
  • Тег id описывает идентификатор. Параметр column указывает на какую колонку в таблице будет ссылаться поле id нашего объекта, так же указываем класс и указываем generator, который отвечает за генерацию id.
  • Тег property описывает простое поле нашего объекта, в качестве параметров указываем имя поля, его класс и имя колонки в таблице.
  • Тег set описывает поле в котором содержится некий набор(коллекция) объектов. Тег содержит параметр name — имя поля нашего объекта, параметр table — имя таблицы связи(в случае отношения многие ко многим) и параметр lazy. Lazy, если меня не подводит моя память, с английского — ленивый. Так называемые ленивые коллекци, сейчас постараюсь объяснить понятнее. Когда мы в параметре lazy указываем значечение false, то у нас при получении объекта Route из базы вместе с объектом достается и коллекция объектов Bus, так как busses это поле объекта Route. А если в качестве параметра мы указываем значение true, то коллекция объектов Bus не вытаскивается, для ее получения надо явно вызывать метод route.getBusses(). Вот предположим такой очень хороший пример. Есть объект город, в него входит массив районов, в каждый район — массив улиц, в каждую улицу — массив домов и так далее до людей, живущих в квартирах. Предположим мы хотим вытянуть из базы названия районов. Если укажем lazy = false, то помимо районом у нас вытянется еще огромный объем «ненужных» данных, если же lazy = true, то мы получим то что надо и ничего лигнего.
  • Тег key имеет параметр column, который говорит, на какую колонку в таблице связи будет ссылаться поле нашего объекта.
  • Тег many-to-many описывает связь типа многие ко многим, в качестве параметров тег использует column — имя колонки второй колонки в таблице связи и параметр class, указывающий какого класса будут объеты на той стороне.

Теперь создадим главный конфигурационный файл hibernate.cfg.xml, файл, откуда он будет дергать всю необходимую ему информацию.

Тут я не буду особо вдаваться в объяснение, думаю многим и так все понятно 🙂 Скажу, что надо только в конце не забыть добавить тег mapping и указать в качестве параметра resources файлы конфигурации ваших бинов.

Теперь создадим класс, который будет хавать наш конфиг-файл и возвращать нам объект типа SessionFactory, который отвечает за создание hibernate-сессии.

import org.hibernate.cfg.Configuration;
import org.hibernate.SessionFactory;

public class HibernateUtil private static final SessionFactory sessionFactory;
static try sessionFactory = new Configuration().configure().buildSessionFactory();
> catch (Throwable ex) System.err.println( «Initial SessionFactory creation failed.» + ex);
throw new ExceptionInInitializerError(ex);
>
>

public static SessionFactory getSessionFactory() return sessionFactory;
>
> * This source code was highlighted with Source Code Highlighter .

Теперь нам осталось разобраться со взаимодействием нашего приложения с базой данных. Для этого для каждого класса-сущности, определим интерфейс, содержащий набор необходимых методов (Я приведу только один интерфейс и одну его реализацию, интерфейсы и реализации для др. классов подобны этим.)

import logic.Bus;
import logic.Driver;
import logic.Route;

import java.util.Collection;
import java.sql.SQLException;

public interface BusDAO public void addBus(Bus bus) throws SQLException;
public void updateBus(Long bus_id, Bus bus) throws SQLException;
public Bus getBusById(Long bus_id) throws SQLException;
public Collection getAllBusses() throws SQLException;
public void deleteBus(Bus bus) throws SQLException;
public Collection getBussesByDriver(Driver driver) throws SQLException;
public Collection getBussesByRoute(Route route) throws SQLException;

Теперь определим реализацию этого интерфейса в классе BusDAOImpl

import DAO.BusDAO;
import logic.Bus;
import logic.Driver;
import logic.Route;
import java.sql.SQLException;
import java.util.Collection;
import java.util. ArrayList ;
import java.util. List ;
import util.HibernateUtil;
import javax.swing.*;
import org.hibernate.Session;
import org.hibernate.Query;

public class BusDAOImpl implements BusDAO

public void addBus(Bus bus) throws SQLException Session session = null ;
try session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.save(bus);
session.getTransaction().commit();
> catch (Exception e) JOptionPane.showMessageDialog( null , e.getMessage(), «Ошибка при вставке» , JOptionPane.OK_OPTION);
> finally if (session != null && session.isOpen())

public void updateBus(Long bus_id, Bus bus) throws SQLException Session session = null ;
try session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.update(bus);
session.getTransaction().commit();
> catch (Exception e) JOptionPane.showMessageDialog( null , e.getMessage(), «Ошибка при вставке» , JOptionPane.OK_OPTION);
> finally if (session != null && session.isOpen()) session.close();
>
>
>

public Bus getBusById(Long bus_id) throws SQLException Session session = null ;
Bus bus = null ;
try session = HibernateUtil.getSessionFactory().openSession();
bus = (Bus) session.load(Bus. class , bus_id);
> catch (Exception e) JOptionPane.showMessageDialog( null , e.getMessage(), «Ошибка ‘findById’» , JOptionPane.OK_OPTION);
> finally if (session != null && session.isOpen()) session.close();
>
>
return bus;
>

public Collection getAllBusses() throws SQLException Session session = null ;
List busses = new ArrayList ();
try session = HibernateUtil.getSessionFactory().openSession();
busses = session.createCriteria(Bus. class ).list();
> catch (Exception e) JOptionPane.showMessageDialog( null , e.getMessage(), «Ошибка ‘getAll’» , JOptionPane.OK_OPTION);
> finally if (session != null && session.isOpen()) session.close();
>
>
return busses;
>

public void deleteBus(Bus bus) throws SQLException Session session = null ;
try session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.delete(bus);
session.getTransaction().commit();
> catch (Exception e) JOptionPane.showMessageDialog( null , e.getMessage(), «Ошибка при удалении» , JOptionPane.OK_OPTION);
> finally if (session != null && session.isOpen()) session.close();
>
>
>

public Collection getBussesByDriver(Driver driver) throws SQLException Session session = null ;
List busses = new ArrayList ();
try session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Long driver_id = driver.getId();
Query query = session.createQuery(
» select b »
+ » from Bus b INNER JOIN b.drivers driver»
+ » where driver.id = :driverId »
)
.setLong( «driverId» , driver_id);
busses = ( List ) query.list();
session.getTransaction().commit();

public Collection getBussesByRoute(Route route) Session session = null ;
List busses = new ArrayList ();
try session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Long route_id = route.getId();
Query query = session.createQuery( «from Bus where route_id = :routeId „ ).setLong( “routeId» , route_id);
busses = ( List ) query.list();
session.getTransaction().commit();

Еще рас скажу, что реализации DriverDAOImpl и RouteDAOImpl будут аналогичны этой.
Наибольший интерес для нас представляют два последних метода, взгляните на них повнимательнее. Как происходит общение с базой? От объекта SessionFactory создается новая или получается текущая сессия, зачем начинается транзакция, выполняются необходимые действия, коммит транзакции и закрытие сессии. Вроде ничего сложного 🙂 Обратите внимание, на то, каким синтаксисом описан запрос к базе. Это так называемый HQL (Hibernate Query Language) HQL представляет собой объектно-ориентированный язык запросов, возможности его широки, но мной настолько широко еще не осилены 🙂 Помимо save, load, update, delete и HQL, можно пользоваться и обычным SQL. Например:

String query = «SELECT driver_id, name, surname, age FROM drivers»;
List drivers = new ArrayList();
drivers = (List) session.createSQLQuery(query).list();

Теперь создадим класс фабрики, к которой будем обращаться за нашими реализациями DAO, от которых и будем вызывать необходимые нам методы.
public class Factory

private static BusDAO busDAO = null ;
private static DriverDAO driverDAO = null ;
private static RouteDAO routeDAO = null ;
private static Factory instance = null ;

public static synchronized Factory getInstance() if (instance == null ) instance = new Factory();
>
return instance;
>

public BusDAO getBusDAO() if (busDAO == null ) busDAO = new BusDAOImpl();
>
return busDAO;
>

public DriverDAO getDriverDAO() if (driverDAO == null ) driverDAO = new DriverDAOImpl();
>
return driverDAO;
>

public RouteDAO getRouteDAO() if (routeDAO == null ) routeDAO = new RouteDAOImpl();
>
return routeDAO;
>
> * This source code was highlighted with Source Code Highlighter .

Теперь нам осталось создать какой-либо демонстрационный класс, для того, чтобы посмотреть и опробовать все то, что мы написали. Ну, не будем тянуть, вот этот класс, возможно не самый удачный, но все же 🙂

public class Main public static void main( String [] args) throws SQLException

Collection routes = Factory.getInstance().getRouteDAO().getAllRoutes();
Iterator iterator = routes.iterator();
System. out .println( «========Все маршруты======== #0000ff»>while (iterator.hasNext()) Route route = (Route) iterator.next();
System. out .println( «Маршрут : » + route.getName() + » Номер маршрута : » + route.getNumber());
Collection busses = Factory.getInstance().getBusDAO().getBussesByRoute(route);
Iterator iterator2 = busses.iterator();
while (iterator2.hasNext()) Bus bus = (Bus) iterator2.next();
System. out .println( «Автобус № » + bus.getNumber());

Collection busses = Factory.getInstance().getBusDAO().getAllBusses();
iterator = busses.iterator();
System. out .println( «========Все автобусы======== #0000ff»>while (iterator.hasNext()) Bus bus = (Bus) iterator.next();
Collection drivers = Factory.getInstance().getDriverDAO().getDriversByBus(bus);
Iterator iterator2 = drivers.iterator();
System. out .println( «Автобус № » + bus.getNumber());
while (iterator2.hasNext()) Driver driver = (Driver) iterator2.next();
System. out .println( «Имя : » + driver.getName() + » Фамилия: » + driver.getSurname());

>
> * This source code was highlighted with Source Code Highlighter .

Еще раз скажу, что может не самый удачный вариант использования всего нами написанного, но для этого уже лучше GUI писать или Web-интерфейс, а это уже другая песня 🙂

P.S.. Скажу, что на 100%-ую правильность я не претендую, это мое личное имхо, однако очень надеюсь, что это станет для кого-то полезным материалом.

P.S.S. Все библиотеки и файлы качать тут.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *