пятница, 31 октября 2014 г.

Пример использования Hibernate

Hibernate — библиотека предназначенная для решения задач объектно-реляционного отображения. Она представляет собой свободное программное обеспечение с открытым исходным кодом (open source), распространяемое на условиях GNU Lesser General Public License. Данная библиотека предоставляет легкий в использовании каркас (фреймворк) для отображения объектно-ориентированной модели данных в традиционные реляционные базы данных. Другими словами, используя данную библиотеку, можно заметно упростить себе жизнь, так как у нас появляется возможность эффективно и легко взаимодействовать с базами данных, не обладая глубокими знаниями SQL. Как использовать Hibernate я расскажу в этой статье.

Кроме Hibernate мы будем использовать в нашем приложении ещё 2 очень распространённых и полезных инструмента: Apache Maven и Spring Framework. Наверняка большинство из вас с ними знакомы, но на всякий случай я кратко расскажу о них.

Apache Maven — фреймворк для автоматизации сборки проектов, специфицированных на XML-языке POM (англ. Project Object Model). Maven обеспечивает декларативную сборку проекта, то есть, в файлах проекта pom.xml содержится его декларативное описание. Все задачи по обработке файлов Maven выполняет через плагины. Maven предоставляет мощные инструменты для управления сложными зависимостями, которые нужны для функционирования проекта. Если вам ещё не приходилось работать с Maven, то вы можете ознакомиться с ним подробнее на официальном сайте http://maven.apache.org.

Spring Framework — универсальный фреймворк с открытым исходным кодом для Java-платформы. Несмотря на то, что Spring Framework не обеспечивает какую-либо конкретную модель программирования, он стал широко распространённым в Java-сообществе главным образом как альтернатива и замена модели Enterprise JavaBeans. В нашем примере он будет использоваться для конфигурирования Hibernate. Если вы ранее не работали со Spring, то я так же настоятельно рекомендую с ним ознакомиться, так как на практике вам придётся довольно часто иметь с ним дело. Официальный сайт проекта http://spring.io.

В качестве примера мы с вами разработаем простой телефонный справочник, который будет хранить список контактов в базе данных, при этом у каждого контакта будет ещё список телефонных номеров. 

В этой статье в качестве базы данных для проекта используется PostgreSQL.

Теперь перейдём непосредственно к разработке. Создайте Maven-проект в любой удобной для вас IDE, лично я предпочитаю Idea IntelliJ, или же можно сгенерировать проект с помощью Maven командой:
 mvn archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \

-DgroupId=tz.com \
-DartifactId=phone-book
После того как проект создан, откройте файл pom.xml и отредактируйте его чтобы он выглядел так:

 <?xml version="1.0" encoding="UTF-8"?>  
 <project xmlns="http://maven.apache.org/POM/4.0.0"  
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>tz.com</groupId>  
   <artifactId>phone-book</artifactId>  
   <version>1.0-SNAPSHOT</version>  
   <packaging>jar</packaging>  
   <properties>  
     <hibernate.version>4.3.6.Final</hibernate.version>  
     <spring.version>4.1.1.RELEASE</spring.version>  
     <postgresql.version>9.1-901-1.jdbc4</postgresql.version>  
   </properties>  
   <dependencies>  
     <!-- Hibernate-->  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-core</artifactId>  
       <version>${hibernate.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-entitymanager</artifactId>  
       <version>${hibernate.version}</version>  
     </dependency>  
     <!-- Spring -->  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-context</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <!-- Spring ORM support -->  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-orm</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>postgresql</groupId>  
       <artifactId>postgresql</artifactId>  
       <version>${postgresql.version}</version>  
     </dependency>  
   </dependencies>  
   <build>  
     <resources>  
       <resource>  
         <directory>${basedir}/src/main/resources</directory>  
       </resource>  
     </resources>  
     <plugins>  
       <plugin>  
         <groupId>org.codehaus.mojo</groupId>  
         <artifactId>exec-maven-plugin</artifactId>  
         <version>1.3.2</version>  
         <configuration>  
           <mainClass>phone.book.main.Main</mainClass>  
         </configuration>  
       </plugin>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-compiler-plugin</artifactId>  
         <version>2.3.2</version>  
         <configuration>  
           <source>1.7</source>  
           <target>1.7</target>  
         </configuration>  
       </plugin>  
     </plugins>  
   </build>  
 </project>  

Давайте посмотрим что содержит наш pom.xml.

<groupId>tz.com</groupId>  
<artifactId>phone-book</artifactId>  
<version>1.0-SNAPSHOT</version> 

Первая важная деталь, это название нашего проекта, артефакт и версия. Зная эту информацию мы можем подключать наш проект как библиотеку к другому проекту, что довольно удобно. Слово SNAPSHOT говорит о том что проект ещё в разработке, поэтому в релизной версии слово SNAPSHOT нужно убрать, оставив только номер версии, например <version>1.0</version>.

Раздел <properties> создан для удобства. Сюда мы вынесли версии библиотек, которые используются в проекте.

Далее в блоке <dependencies> у нас идёт список зависимостей для нашего проекта. В него я включил библиотеки для работы с Hibernate и Spring, а так же библиотеку для работы с базой данных PostgreSQL:

   <dependencies>  
     <!-- Hibernate-->  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-core</artifactId>  
       <version>${hibernate.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.hibernate</groupId>  
       <artifactId>hibernate-entitymanager</artifactId>  
       <version>${hibernate.version}</version>  
     </dependency>  
     <!-- Spring -->  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-context</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <!-- Spring ORM support -->  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-orm</artifactId>  
       <version>${spring.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>postgresql</groupId>  
       <artifactId>postgresql</artifactId>  
       <version>${postgresql.version}</version>  
     </dependency>  
   </dependencies>  

Ну и последний блок содержит плагины, необходимые для нашего проекта:

     <plugins>  
       <plugin>  
         <groupId>org.codehaus.mojo</groupId>  
         <artifactId>exec-maven-plugin</artifactId>  
         <version>1.3.2</version>  
         <configuration>  
           <mainClass>phone.book.main.Main</mainClass>  
         </configuration>  
       </plugin>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-compiler-plugin</artifactId>  
         <version>2.3.2</version>  
         <configuration>  
           <source>1.7</source>  
           <target>1.7</target>  
         </configuration>  
       </plugin>  

Хочу обратить внимание на exec-maven-plugin. Он позволяет запускать наш проект средствами Maven, что будет для нас крайне полезно. Для этого в директории проекта достаточно выполнить команду:
mvn exec:java
Обратите внимание что мы сделали ссылку на main-класс для exec-maven-plugin, который пока что не создали: <mainClass>phone.book.main.Main</mainClass>.

Теперь создайте в каталоге /src/main/resources/META-INF/sping файл beans.xml со следующим содержанием:

 <?xml version="1.0" encoding="UTF-8"?>  
 <beans xmlns="http://www.springframework.org/schema/beans"  
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
     xmlns:tx="http://www.springframework.org/schema/tx"  
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">  
   <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
     <property name="driverClassName" value="org.postgresql.Driver" />  
     <property name="url" value="jdbc:postgresql://localhost:5432/test_db" />  
     <property name="username" value="postgres" />  
     <property name="password" value="postgres" />  
   </bean>  
   <!-- Hibernate 4 SessionFactory Bean definition -->  
   <bean id="hibernateSessionFactory"  
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
     <property name="dataSource" ref="dataSource" />  
     <property name="annotatedClasses">  
       <list>  
         <value>phone.book.model.Person</value>  
         <value>phone.book.model.Phone</value>  
       </list>  
     </property>  
     <property name="hibernateProperties">  
       <props>  
         <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>  
         <prop key="hibernate.current_session_context_class">thread</prop>  
         <prop key="hibernate.show_sql">false</prop>  
         <prop key="hibernate.hbm2ddl.auto">update</prop>  
       </props>  
     </property>  
   </bean>  
   <bean id="personDAO" class="phone.book.dao.PersonDaoImpl">  
     <property name="sessionFactory" ref="hibernateSessionFactory" />  
   </bean>  
 </beans>  

В beans.xml мы сконфигурировали DataSource, который содержит всю необходимую информацию для подключения к базе данных: класс JDBC-драйвера (в данном случае драйвер для PostgreSQL), URL, логин и пароль.
Затем мы создаём бин hibernateSessionFactory, используя класс LocalSessionFactoryBean из библиотеки Spring ORM. Этот класс позволяет нам сконфигурировать все настройки Hibernate в спринге, и инициализирует класс SessionFactory, с помощью которого осуществляется взаимодействие с базой данных.
Для бина hibernateSessionFactory мы задаём проперти, которые используются для инициализации SessionFactory:

  • DataSource, без которого мы не сможем установить подключение к базе данных.
  • Классы-сущности Hibernate, в проперти "annotatedClasses". Они представляют собой описание структуры таблиц базы данных в виде объекта. Эти классы мы скоро создадим.
  • Настройки Hibernate в проперти "hibernateProperties". Тут 2 очень важных момента, а именно это SQLDialect (в данном случае PostrgeSQLDialect), который должен быть выбран или определён в соответствии с той базой данных, которую мы планируем использовать, и параметр "hibernate.hbm2ddl.auto" который определяет может ли Hibernate создавать таблицы или редактировать их структуру, если их нет или они отличаются о того что описано в классах-сущностях. У нас параметр "hibernate.hbm2ddl.auto" имеет значение "update", то есть в случае отсутствия необходимых таблиц в базе данных Hibernate их создаст. 

Последним шагом мы конфигурируем в спринге класс PersonDaoImpl и передаём ему объект SessionFactory в проперти.

Теперь в каталоге /src/main/java создайте пакет phone.book.dao и поместите туда интерфейс и класс, что описаны ниже:

Интерфейс PersonDao.java:
 package phone.book.dao;  
 import phone.book.model.Person;  
 import java.util.List;  
 
 public interface PersonDao {  
   public void save(Person p);  
   public List<Person> getPersonList();  
 }  

Класс PersonDaoImpl.java:
 package phone.book.dao;  
 import phone.book.model.Person;  
 import java.util.List;  
 import org.hibernate.Session;  
 import org.hibernate.SessionFactory;  
 import org.hibernate.Transaction;  
  
 public class PersonDaoImpl implements PersonDao {  
   private SessionFactory sessionFactory;  
   public void setSessionFactory(SessionFactory sessionFactory) {  
     this.sessionFactory = sessionFactory;  
   }  
   @Override  
   public void save(Person person) {  
     Session session = this.sessionFactory.openSession();  
     Transaction tx = session.beginTransaction();  
     session.persist(person);  
     tx.commit();  
     session.close();  
   }  
   @Override  
   public List<Person> getPersonList() {  
     Session session = this.sessionFactory.openSession();  
     String hql = "from Person";  
     List<Person> personList = session.createQuery(hql).list();  
     session.close();  
     return personList;  
   }  
 }  

Класс PersonDaoImpl умеет всего 2 вещи: сохранять объект Person в базу данных и получать из базы данных все сохранённые объекты Person. Класс Person описывает структуру таблицы PERSON из нашей базы данных. Забегая вперёд, скажу что у нас ещё будет таблица PHONES.
Давайте создадим классы для этих таблиц в пакете phone.book.model:

Класс Person.java:
 package phone.book.model;  
 import javax.persistence.*;  
 import java.util.List;  
  
 @Entity  
 @Table(name="PERSON")  
 public class Person {  
   @Id  
   @Column(name="id")  
   @GeneratedValue(strategy=GenerationType.IDENTITY)  
   private Long id;  
   private String name;  
   @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)  
   private List<Phone> phones;  
   public Person() {  
     super();  
   }  
   public Person(String name, List<Phone> phone) {  
     this.name = name;  
     this.phones = phone;  
   }  
   public Long getId() {  
     return id;  
   }  
   public void setId(Long id) {  
     this.id = id;  
   }  
   public String getName() {  
     return name;  
   }  
   public void setName(String name) {  
     this.name = name;  
   }  
   public List<Phone> getPhones() {  
     return phones;  
   }  
   public void setPhones(List<Phone> phone) {  
     this.phones = phone;  
   }  
   @Override  
   public String toString() {  
     return "Person{" +  
         "id=" + id +  
         ", name='" + name + '\'' +  
         ", phones=" + phones +  
         '}';  
   }  
 }  

Класс Phone.java:
 package phone.book.model;  
 import javax.persistence.*;  

 @Entity  
 @Table(name = "PHONES")  
 public class Phone {  
   @Id  
   @Column(name="id")  
   @GeneratedValue(strategy=GenerationType.IDENTITY)  
   private Long id;  
   private String phoneNumber;  
   public Phone() {  
     super();  
   }  
   public Phone(String phoneNumber) {  
     this.phoneNumber = phoneNumber;  
   }  
   public Long getId() {  
     return id;  
   }  
   public void setId(Long id) {  
     this.id = id;  
   }  
   public String getPhoneNumber() {  
     return phoneNumber;  
   }  
   public void setPhoneNumber(String phoneNumber) {  
     this.phoneNumber = phoneNumber;  
   }  
   @Override  
   public String toString() {  
     return "Phone{" +  
         "id=" + id +  
         ", phoneNumber='" + phoneNumber + '\'' +  
         '}';  
   }  
 }  

Классы-сущности Person и Phone заслуживают особого внимания, как я уже говорил, они являются объектным представлением таблиц базы данных для фреймворка Hibernate. Любой класс-сущность должен начинаться с аннотации @Entity. Далее в коде следует аннотация @Table, содержащая в качестве параметра название таблицы, которую описывает класс-сущность. Аннотация @Id указывает на то, что данное поле является индексом, аннотация @Column определяет соответствие переменной конкретному столбцу таблицы, а аннотация @GeneratedValue указывает на то что значение этого поля будет генерироваться автоматически.
У нас есть ещё один очень интересный момент. Дело в том что таблицы PERSON и PHONES связанны между собой связью "один ко многим", то есть у одного человека может быть несколько номеров телефона. Эта связь описана с помощью аннотации @OneToMany.

У нас всё готово для работы с базой данных, теперь осталось написать класс, который будет использовать всё что у нас уже есть. Создайте пакет phone.book.main и поместите туда главный класс для нашего приложения:

Main.java:
 package phone.book.main;  
 import phone.book.dao.PersonDao;  
 import phone.book.model.Person;  
 import org.springframework.context.support.ClassPathXmlApplicationContext;  
 import phone.book.model.Phone;  
 import java.util.ArrayList;  
 import java.util.List;  
 
 public class Main {  
   public static void main(String[] args) {  
     ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/beans.xml");  
     PersonDao personDAO = context.getBean(PersonDao.class);  
     ArrayList<Phone> personArrayList = new ArrayList<Phone>();  
     personArrayList.add(new Phone("80001112222"));  
     Person person = new Person("alexey", personArrayList);  
     personDAO.save(person);  
     System.out.println("Person::"+person);  
     List<Person> list = personDAO.getPersonList();  
     for(Person p : list){  
       System.out.println("Person List::"+p);  
     }  
     //close resources  
     context.close();  
   }  
 }  

Тут всё довольно просто: мы получаем контекст спринга с помощью ClassPathXmlApplicationContext, достаём из контекста наш ДАО, инициализируем классы сущности с константными значениями и сохраняем информацию из этих классов в базе данных. Затем выводим на экран все контакты, которые сохранены в базе данных.

На всякий случай выкладываю скрин с общей структурой нашего проекта:



Чтобы увидеть результат работы, соберите проект с помощью команды:
mvn install
И запустите приложение с помощью команды:
mvn exec:java
Исходники проекта можно найти тут: https://github.com/AlexeyKutepov/phone-book/
Желаю вам приятного пользования =)

С уважением, Алексей Кутепов



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

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

Яндекс.Метрика