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

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


Date of publication 12.09.2016



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>
        <!-- for 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 and Transactions -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</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>
                <!-- Build an executable JAR -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>phone.book.main.Main</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <version>2.3.7</version>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                        <Import-Package>
                            org.springframework.orm,
                            org.springframework.context,
                            javax.persistence,
                            org.hibernate,
                            *
                        </Import-Package>
                        <Export-Package>
                        </Export-Package>
                    </instructions>
                </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("89200868942"));

        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/