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

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

В качестве примера мы с вами разработаем простой телефонный справочник, который будет хранить список контактов в базе данных (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>  

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

Так же хочу обратить внимание на 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 и поместите туда главный класс для нашего приложения:

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, достаём из контекста наш ДАО, инициализируем классы сущности с константными значениями и сохраняем информацию из этих классов в базе данных. Затем выводим на экран все контакты, которые сохранены в базе данных.

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

Hibernate-example-project.png

Чтобы увидеть результат работы, соберите проект с помощью команды:

mvn install

И запустите приложение с помощью команды:

mvn exec:java

 

Исходники проекта можно найти тут: https://github.com/AlexeyKutepov/phone-book/

Hibernate Java

blog comments powered by Disqus