Туторіал по Hibernate з анотаціями з використанням Spring та Maven

Для виконання цього туторіалу необхідні наступні речі:

1) Eclipse IDE 3.4+ з додатками Spring IDE, m2Eclipse (деталі установки можна знайти в Spring MVC туторіалі).

2) Maven 2+

3) Java 5+

4) MySQL 5+

Ці інструменти повинні бути налаштовані та робочі.

Почнемо з створення проекту. В Екліпсі слід вибрати File->New->Other->Maven Project.

Create a simple project повинен бути не чектнутий. При потребі можна вказати розташування проекту, відчекнувши Use default Workspace location.

На наступному діалозі слід вибрати архетип maven-archetype-quickstart. Після вибору архетипу необхідно вказати ідентифікатор групи: com.rozrobka та артефакту: hiberdemo.

Коли все вказано, Ексліпс створить проект.

Для того щоб підключити Hibernate та Spring до pom.xml файлу, в корені проекту, слід додати наступний текст у групу :

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring</artifactId>
        <version>2.5.6</version>
    </dependency>    
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate</artifactId>
        <version>3.2.6.ga</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-annotations</artifactId>
        <version>3.3.0.ga</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-commons-annotations</artifactId>
        <version>3.3.0.ga</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.2.2</version>
    </dependency>

Це вкаже Maven-у використовувати Hibernate, MySQL бібліотеки та Spring як залежності для проекту.

Можна приступати до створення класів. Моделі будуть розміщуватись у пакеті com.rozrobka.model. Для зручності створимо маркер інтерфейс DataEntity, який будуть реалізовувати класи моделі.

Предметна область у цьому прикладі доволі проста — Оголошення (ad) яке належить до Категорії (category) у співвідношенні багато-до-одного (many-to-one).

Створимо першу модель Category:

package com.rozrobka.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="categories")
public class Category implements DataEntity {

    @Id
    @GeneratedValue
    @Column(name="id")
    private Integer id;

    @Column(name="name")
    private String name;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

Ця модель використовує анотації з пакетів JPA. Hibernate надає реалізації цих анотації, які сумісні з специфікаціями JPA.

Анотація @Entity вказує що цей клас є сутністю. Анонтація @Table дозволяє керувати налаштуваннями мапування сутності на таблицю. В нашому випадку ми вказуємо що сутності Category будуть зберігатись у таблиці caregories.

Далі ідуть анотації рівня полів. @Id вказує що поле є ключовим. @GeneratedValue вказує що значення поля генерується базою даних, наприклад авто-інкремент. Анотація @Column допомагає вказати характеристики поля бази даних, наприклад назву, довжину, можливість пустих значень та інше.

Створимо наступну модель Ad:

package com.rozrobka.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="ads")
public class Ad implements DataEntity {

    @Id
    @GeneratedValue
    @Column(name="id")
    private Integer id;

    @Column(name="name", nullable=false)
    private String name;

    @Column(name="description")
    private String description;

    @Column(name="email")
    private String email;

    @JoinColumn(name = "category_id")
        @ManyToOne(optional = false)
    private Category category;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Category getCategory() {
        return category;
    }
    public void setCategory(Category category) {
        this.category = category;
    }
}

Цей клас представляє оголошення і має звязок з класом Category. Використані подібні анотації як і для Category.

Крім стандартних, для мапування використана анотація @JoinColumn, яка вказує поле яке відповідає за звязок на рівні бази даних, та анотація @ManyToOne, яка вказує мапування на логічному рівні класів.

Обидва класи моделі мають відповідати специфікації JavaBeans, тобто мати правильно проіменовані гетери та сетери та інші речі.

Моделі готові, можна приступити до налаштування Spring.

Для цього слід створити папку resources у папці src/main. У ній слід створити файл appCtx.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:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd"> 

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation">
            <value>classpath:/hibernate.cfg.xml</value>
        </property>
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>
        <property name="hibernateProperties" ref="hibernatePropertiesConfigurer"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${database.driverClass}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
    </bean> 

    <util:properties id="hibernatePropertiesConfigurer" location="classpath:/hibernate.properties"/>

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:/database.properties</value>
            </list>
        </property>
    </bean>

    <bean id="adsDao" class="com.rozrobka.hiberdemo.AdsDao">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

Це є контекст файл Spring, у якому описуються класи та звязки між ними. Давайте пройдемось по головним бінам.

sessionFactory описує допоміжний Spring клас AnnotationSessionFactoryBean який дає можливість використовувати анотовані моделі. Йому вказуються файли налаштувань та джерело даних.

dataSource визначає джерело даних. Параметри підключення автоматично вказуються Spring-ом на етапі запуску.

hibernatePropertiesConfigurer та propertyConfigurer є допоміжними бінами які допомагають вказати параметри налаштувань Hibernate та джерела даних.

adsDao це є наш бін, який буде містити логіку доступу до бази даних. Його ми зараз створимо.

Для цього у пакеті com.rozrobka.hiberdemo слід стоврити клас AdsDao, який розширяє клас HibernateDaoSupport:

package com.rozrobka.hiberdemo;

import java.util.List;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.rozrobka.model.Ad;
import com.rozrobka.model.DataEntity;

public class AdsDao extends HibernateDaoSupport {

    public void save(DataEntity entity) {
        getHibernateTemplate().save(entity);
    }

    public List<Ad> getAllAds() {
        return getHibernateTemplate().loadAll(Ad.class);
    }

}

Клас містить два методи: save та getAllAds. За допомогою save можна зберігати всі класи, які реалізують інтерфейс DataEntity, у нашому випадку це Ad та Category.

Метод getAllAds вертає з бази даних всі сутності типу Ad. метод getHibernateTemplate надається суперкласом HibernateDaoSupport і є зручним та практичним шляхом взаємодії з Hibernate.

Реалізація готова. Тепер слід доналаштувати Hibernate та параметри підключення до бази даних.

Для цього створимо у папці resources файл hibernate.cfg.xml з наступним вмістом:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <mapping class="com.rozrobka.model.Ad" />
        <mapping class="com.rozrobka.model.Category" />
    </session-factory>
</hibernate-configuration>

Це є конфігураційний файл Hibernate. У нашому випадку він є малим, так як ми винесли налаштування підєднання до бази даних окремо. Він описує два класи, які є сутностями Hibernate.

Для опису необхідно вказувати канонічну назву класу з назвою пакету.

Далі слід створити додатковий конфігураційний файл hibernate.properties, який міститиме налаштування, які можуть мінятись з часом:

hibernate.show_sql=true
hibernate.dialect=org.hibernate.dialect.MySQLDialect
# or validate
hibernate.hbm2ddl.auto=update

Цей файл містить три налаштування.

show_sql вказує чи виводити до логу запити які робить Hibernate.

dialect вказує який використовувати діалект SQL. у нашому випадку це діалект MySQL. Для інших типів баз даних слід вибрати інший діалект. Діалект має вплив на згенерований SQL код.

hbm2ddl.auto вказує поведінку при роботі з структурою бази даних. update означає що Hibernate сам згенерує структуру на базі мапувань, або анотації. validate означає, що Hibernate просто перевірить структуру на відповідність моделям.

Далі створимо конфігураційний файл бази даних database.properties:

database.driverClass=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost/hiberdemo
database.username=user
database.password=password

Приступимо до самої аплікації.

Слід змінити вміст класу App з пакету com.rozrobka.hiberdemo на наступний:

package com.rozrobka.hiberdemo;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.rozrobka.model.Ad;
import com.rozrobka.model.Category;

/**
 * Hello world!
 *
 */
public class App {
    public static void main( String[] args ) {
        String[] configs = {"classpath:/appCtx.xml"};
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

        AdsDao adsDao = (AdsDao) ctx.getBean("adsDao");

        Category cat1 = new Category();
        cat1.setName("First Category");

        adsDao.save(cat1);

        Ad ad1 = new Ad();
        ad1.setCategory(cat1);
        ad1.setName("Ad 1");
        ad1.setEmail("mail@inet.com");
        ad1.setDescription("description");

        Ad ad2 = new Ad();
        ad2.setCategory(cat1);
        ad2.setName("Ad 2");
        ad2.setEmail("mail@inet.com");
        ad2.setDescription("description");

        adsDao.save(ad1);
        adsDao.save(ad2);

        List<Ad> ads = adsDao.getAllAds();
        System.out.println("Array size: " + ads.size());

    }
}

Давайте розберемо цей код.

String[] configs = {"classpath:/appCtx.xml"};
        ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);

        AdsDao adsDao = (AdsDao) ctx.getBean("adsDao");

Вказування розташування файлу контексту, та ініціалізація Spring контексту. ctx.getBean вертає бін по імені з ініціалізованого контексту.

Повернений бін це є наше DAO яке пряцює з Hibernate.

Решта коду взаємодіять з цим DAO: створення та збереження однієї категорії, та створення двох оголошень, визначення звязку з категорією та подальше збереження.

Останні дві стрічки роблять запит до бази даних та перевіряють чи дійсно два оголошення збереглись.

Перед запуском слід створити саму базу даних:

create database hiberdemo;

Коли все готово, запускаємо клас App!

Має бути багато логів на виході, які закінчаться приблизно такими стрічками:

Hibernate: insert into categories (name) values (?)
Hibernate: insert into ads (CLIENT_ID, description, email, name) values (?, ?, ?, ?)
Hibernate: insert into ads (CLIENT_ID, description, email, name) values (?, ?, ?, ?)
Hibernate: select this_.id as id0_1_, this_.CLIENT_ID as CLIENT5_0_1_, this_.description as descript2_0_1_, this_.email as email0_1_, this_.name as name0_1_, category2_.id as id1_0_, category2_.name as name1_0_ from ads this_ inner join categories category2_ on this_.CLIENT_ID=category2_.id
Array size: 2

Cлід памятати що після створення схеми, слід змінити конфігурацію:

hibernate.hbm2ddl.auto=validate

Код доступний на моєму сайті. Приємних експериментів.

Коментарі 15

afon - 28 липня 2009, 18:58

Все прикольно, почти, как по часам.

Кстати, я нигде не увидел подключения и конфигурирования логов. При таком раскладе спринги с хибернейтом должны массово ругаться на stdout о невозможности обнаружить реализацию логирования.

Да, еще, на сколько я помню, для данного примера не обязательно указывать аннотацию @Column(name=«ххх»), т.к. поля одноименные.

Но в общем респект. Хороший туториал.

zenyk - 29 липня 2009, 13:59

дякую за відгук :)

щодо логів — так, їх буде багато і всі вони підуть в stdout. я спеціально не перевантажував туторіал логуванням, так як планую якось окремо описати підходи до логування. плюс початківцям думаю цікаво буде почитати що відбувається (принаймні мені було цікаво в свій час :) )

Так, для наглядності мабуть треба було декілька «name» забрати, так як JPA/Hibernate має багато конвеншинів та значень по замовчуванню.

afon - 29 липня 2009, 15:34

планую якось окремо описати підходи до логування вот это было бы реально круто. Ну, мне по крайней мере, т.к. обычно конфигурят только log4j, а остальными системами как-то не интересуются. Мне бы было интересно.

lemon - 23 липня 2009, 15:37

а де малюнки?!?! :D

zenyk - 27 липня 2009, 11:57

один на початку є :)

serter - 02 серпня 2009, 14:29

конструктивніше було б використати Generic DAO, замість звичайних

zenyk - 03 серпня 2009, 13:04

мається на увазі не наслідувати HibernateDaoSupport? чи використати інший клас?

serter - 03 серпня 2009, 13:11

наприклад так:

class BasicDAO<T> extends HibernateDaoSupport {
...
}
class AdsDao extends BasicDAO<Ad> {
...
}
zenyk - 03 серпня 2009, 13:18

а… так, звичайно :)

я не хотів сильно ускладнювати цей туторіал і старався тримати кількість класів малою.

для справжніх проектів, звичайно що треба використовувати універсальні DAO, у випадках подібних до цього

zenyk - 03 серпня 2009, 13:06

чи щось типу цього?

serter - 03 серпня 2009, 13:12

типу так, але в спрінгу це вже є в HibernateDaoSupport

zenyk - 03 серпня 2009, 13:05

ще цікавий допис по темі — Using Hibernate Annotations and Maven

afon - 17 серпня 2009, 12:51

Не вистачає ліби commons-pool, що потрібна для ініціалізації dataSource біна. Додайте будьласка в опис.

GrAndSE - 06 вересня 2009, 07:13

Жалію, що пропустив цей матеріал раніше.

msangel - 03 грудня 2012, 09:12

карочи я читыри часа искал нармальную статью в интернете штоб токо была аб хибернейте, мивине, мискьюэле НО БЕЗ ЭТОГО СПРИНГА. карочи спасиба тибе мужик за статью я ужде навверное с чистай сивестью пайду спать.

годная, очень даже годная статья. с миня сотни нефти.

Коментувати
© 2009 - 2020, Розробка - соціальна ІТ спільнота.
Контакти: info@rozrobka.com
Правила користування