Нам нужно создать наш первый проект. Вы можете скопировать
пример из исходников, которые можно скачать отсюда https://code.google.com/p/code-vit/issues/detail?id=2.
Практически всегда, я пользуюсь при
создании нового проекта, maven’ом , ввожу команду mvn archetype:generate в
командной строчке и ищу подходящий мне archetype. Потом я из него
делаю шаблон, с нужными мне зависимостями и после создаю свой archetype командой mvn archetype:create . Устанавливаю его в локальный репозиторий, на диске он будет
находится в каталоге ~/.m2/archetype-catalog.xml. Я советую вам
поступать подобным образом, у вас всегда будет под рукой нужный шаблон проекта,
с нужными вам библиотеками и загруженными зависимостями в локальном каталоге maven. Для простоты рассмотрим пример из исходника ch1, сначала попробуем разобраться в наших
зависимостях. Откроем pom.xml найдем в нем <dependencies> это будут
наши зависимости, которые нам нужны для работы hibernate:
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.7.Final</version>
</dependency>
<!--
Hibernate logging -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<!-- The tutorials use the H2
in-memory database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.2.145</version>
</dependency>
</dependencies>
В первой зависимости мы подключаем hibernate-core набор минимальных библиотек для работы с hibernate. Во второй и третьей зависимости мы подключаем
логирование для ведения лога отладки, который
поможет нам понять, что же твориться
внутри hibernate. В четвертой зависимости мы загружаем библиотеку
базы данных H2, которая
полностью написана на java
и которая на первом этапе поможет нам избежать ненужной настройки и установки базы данных, такой как mysql или postgresql на пример. Перейдите в каталог code\c2\src\main\resources
, вы тут найдите файл hibernate.cfg.xml. Это основной конфигурационный файл Hibernate хранящий настройки, давайте перечислим самые часто используемые из
них:
Свойство
|
Описание
|
connection.driver_class
|
Драйвер, используемый для подключения к
базе данных
|
connection.url
|
Строка подключения к базе данных
|
connection.username
|
Имя пользователя базы данных
|
connection.password
|
Пароль пользователя базы данных
|
dialect
|
Диалект базы данных, который будет
использоваться hibernate,
вы наверное знаете что есть международные стандарты sql языка, но у каждой базы данных своя
реализация, и соответственно некоторые стандарты могут быть не реализованы, или реализованы частично,
или специфически. Вот что бы предусмотреть
нестандартные, специфические команды sql
конкретной базы данных и ввели в hibernate диалект. Перечислю несколько из них: H2Dialect, MySQL5Dialect, Oracle10gDialect,
PostgreSQL81Dialect. Полный список
диалектов вы можете найти в пакете org.hibernate.dialect.*
|
show_sql
|
При разработке и тестировании
приложения, всегда бывает полезно, знать какие действия выполняет Hibernate. Включает логирование действий Hibernate, очень полезная
вещь, всегда советую включать при разработке приложения.
|
hbm2ddl.auto
|
Включает создание таблиц базы данных,
на основе наших классов сущностей.
|
hibernate.jdbc.fetch_size
|
Размер выборки данных из
результирующего набора данных JDBC.
Допустим, hibernate отправил sql запрос на выборку данных и в него, попало
100 записей. Размер выборки 20. Hibernate потребуется 5 раз сделать выборку из
результирующего набора JDBC.
|
hibernate.jdbc.batch_size
|
Указывает Hibernate сколько
операций обновления нужно сгруппировать в “пакет” и отправить их всех вместе. Допустим
у нас изменилось 20 записей. Размер группировки 5, т.е. каждые 5
записей будут группироваться в пакет, и уходить на обновление в базу данных,
всего будет сделано 4 обновления.
|
max_fetch_depth
|
Устанавливает глубину выборки зависимых
классов, т.е. сколько зависимых
классов будет заполнено данными. Этот
параметр может помочь избежать
ненужной выборки данных для классов, и выполнения излишних sql запросов к
базе данных, так как иногда бывает, не известно нужна ли будет информация по
зависимым классам, или нет.
|
В файле log4j.properties находятся настройки для логирования проекта в
целом и в частности Hibernate.
Давайте создадим класс Person , который
будет отображать одну запись из таблицы Persons в базе данных H2. Таблица Persons будет содержать уникальный ключ personId, имя – fName и фамилию
– sName. Нам нужно как-то сопоставить таблицу Persons и поля таблицы, с классом Person и полями класса, в этом нам помогут аннотации.
Давайте взглянем на класс Person.java :
package
org.vit.ch1;
import javax.persistence.*;
import java.io.Serializable;
@Entity @Table(name = "Persons")
public class Person implements Serializable {
static final
long serialVersionUID = -7593775012501239455L;
@Id @GeneratedValue(strategy =
GenerationType.AUTO) @Column(name =
"personId")
private Long
id;
@Column(name
= "fName")
private
String firstName;
@Column(name
= "sName")
private
String secondName;
public Person() {
}
…
getters and
setters
…
@Override
public
String toString() {
return
"Person{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", secondName='" + secondName + '\'' +
'}';
}
@Override
public
boolean equals(Object o) {
if (this
== o) return true;
if (!(o
instanceof Person)) return false;
Person
person = (Person) o;
if
(firstName != null ? !firstName.equals(person.firstName) : person.firstName !=
null) return false;
if (id
!= null ? !id.equals(person.id) : person.id != null) return false;
if
(secondName != null ? !secondName.equals(person.secondName) : person.secondName
!= null) return false;
return
true;
}
@Override
public int hashCode() {
int
result = id != null ? id.hashCode() : 0;
result =
31 * result + (firstName != null ? firstName.hashCode() : 0);
result =
31 * result + (secondName != null ? secondName.hashCode() : 0);
return result;
}
}
Класс Person представляет собой POJO класс, отображение таблицы на класс называется сущностью. Сущность можно реализовать двумя способами.
Первый способ это сначала отдельно создать таблицу Persons и связать ее с POJO классом Person.java . И второй способ это
создать сначала POJO класс Person и включить в конфигурационном файле hibernate.cfg.xml параметр hbm2ddl.auto, который попросит Hibernate сгенерировать схемы таблиц базы данных
на основе POJO класса. Пойдем наипростейшим путем, выберем второй способ и поставим значение в
свойстве hbm2ddl.auto равным create, которое будет постоянно требовать, пересоздавать, пустую таблицу Persons при каждом запуске
нашей программы. При окончательном
развертывании приложения в продуктивной среде, я бы вам советовал все
ключи, индексы, триггеры, перечисления именовать вручную
с подходящими по смыслу именами. И к ним же добавить все схемы таблиц баз
данных т.е. прописать в отдельном sql файле
весь сценарий создания базы данных. Продолжим, и так что бы
наш класс Person правильно
взаимодействовал с hibernate и стал сущностью, нужно, во-первых применить к нему аннотации и
второе POJO объект
должен удовлетворять следующим требованиям:
1. Иметь конструктор по умолчанию без параметров.
2.
Переопределять методы toString(), Equals(), HashCode().
3. Иметь поле "первичного ключа" (id) как минимум, как максимум сложный
составной ключ, это условие не обязательно,
сущность может и не иметь ключа, но это чревато большими проблемами.
4. Класс сущности не должен быть объявлен как
final класс.
5. Класс сущности должен наследовать интерфейс
Serializable.
6. Должны быть getters и setters методы как у любого POJO класса.
Немножко отступим от темы и поговорим о интерфейсе
Serializable. С каждым сериализуемым классом связан уникальный
идентификационный номер serialVersionUID. Если вы не указываете этот
идентификатор явно, декларируя поле
private static final long с названием serialVersionUID, система
генерирует его автоматически, используя
для класса сложную схему расчетов. При этом на автоматически генерируемое
значение оказывают влияние название класса, названия реализуемых им
интерфейсов, а также все открытые и защищенные члены. Если вы каким-то образом
поменяете что-либо в этом наборе, например, добавите новый метод, изменится и
автоматически генерируемый serial versionUID. Следовательно, если вы не будете явным образом
декларировать этот идентификатор, совместимость с предыдущими версиями будет
потеряна. Клиенты, которые пытаются сериализовать объект с помощью старой
версии класса и десериализовать его уже с помощью новой версии, получат сбой программы.
И так, как создать static final long serialVersionUID? Пусть у нас
есть скомпилированный класс src/org/vit/Person.class
перейдем в папку src и запустим команду serialver
которая входит в jdk:
C:\...\src>serialver org.vit.ch1.Person
org.vit.ch1.Person:
static final long serialVersionUID =
-7593775012501239455L;
C:\...\src>
копируем и вставляем в наш класс:
public class Person implements Serializable{
static final
long serialVersionUID = -7593775012501239455L;
...
}
Осталось только поговорить об аннотациях. То, что класс является сущностью, и с ней будет работать Hibernate, говорит нам аннотация @Entity. На какую таблицу базы
данных будет отображаться наш POJO класс, говорит нам аннотация @Table(name
= "Persons"),
параметр name задает
нам имя таблицы. Если имя таблицы базы данных не задано в атрибуте name, то имя класса и будет именем таблицы. Любая таблица баз данных должна иметь
уникальный первичный ключ для того, что бы по ключу можно было найти запись и
идентифицировать её. Hibernate будет использовать этот ключ как уникальный идентификатор,
при работе с объектами сессии. Ключ
объявляется аннотацией @Id. Любой ключ должен быть сгенерирован по
каким-нибудь правилам, аннотация @GeneratedValue(strategy
= GenerationType.AUTO) отвечает за генерацию ключа . Атрибут strategy отвечает за метод генерации
ключа. Допустим SEQUENCE использует последовательность (sequence) в PostgreSQL, Oracle, у этих баз данных автоинкремент поля, реализован через перечисление. IDENTITY реализует, identity колонки в MySQL, MS SQL Server. Для нашего примера установим атрибут strategy в значение GenerationType.AUTO т.е hibernate сам решит какую стратегию генерации ключа
лучше выбрать для нашего диалекта базы данных H2 (org.hibernate.dialect.H2Dialect). Осталось рассказать о последней аннотации @Column(name = "personId"). Эта аннотация соотносит, другими словами делает маппинг полей класса на поля таблицы базы данных, для
нашего примера поле Id
класса Person будет соотнесено полю
personId таблицы Persons. И последние, для hibernate нужно указать в файле настроек hibernate.cfg.xml
строчку, с каким классом hibernate должен работать как с сущностью:
<!-- Names the annotated entity class -->
<mapping
class="org.vit.ch1.Person"/>
Осталось нам взглянуть, как работает hibernate, и реализовать 2 операции это вставка записи в
базу данных, и её чтение. Основной
рабочей лошадкой в Hibernate является объект Session. Чтобы получить session, нужно создать фабрику классов SessionFactory. Для создания фабрики классов нужен файл hibernate.cfg.xml, откуда будут считаны все
конфигурационные настройки. Я всегда
стараюсь использовать его, по умолчанию hibernate сначала
ищет файл настроек hibernate.properties, который в большем приоритете, чем файл
hibernate.cfg.xml, это нужно иметь ввиду
если вы пользуетесь двумя файлами настройки. В вашей программе может быть создано несколько
объектов SessionFactory с различными
конфигурациями, на пример, можете
настроить 2 конфигурационных файла hibernate.cfg.xml для работы с различными базами данных одновременно. У объекта SessionFactory мы должны запросить
объект session, который
будет создан или возвращен нам из пула
объектов SessionFactory. Зачем нам нужен объект SessionFactory? Он
управляет жизненным циклом объектов session. Как получили
сессию, всегда работа с сессией начинается с запроса новой транзакции (.beginTransaction). Транзакция должна быть подтверждена(.commit) или отменена(.rollback). При отмене транзакции все
данные откатываются до состояния начала транзакции. После того как мы завершили работу с объектом session мы должны его закрыть
(.close),
вернуть ресурсы объекту SessionFactory. Внутри
транзакции мы можем применить к объекту
класса Person операции: создать данные в таблице (.save),
считать данные в объект Person (.get) по ключу id, удалить данные (.delete). И так
взглянем на пример:
public class
First {
private
SessionFactory sessionFactory;
public
First() {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = null;
try
{
session = sessionFactory.openSession();
session.getTransaction().begin();
Person person = new Person();
person.setFirstName("Vitaly");
person.setSecondName("Lopanov");
session.save(person);
System.out.printf("Insert Person: %s\n", person);
Long
id = person.getId();
session.getTransaction().commit();
session.close();
session = sessionFactory.openSession();
session.getTransaction().begin();
Person person2 = (Person) session.get(Person.class, id);
System.out.printf("Person select:
%s\n", person2);
session.getTransaction().commit();
}
finally {
if
(session!=null) session.close();
}
}
finally {
if
(sessionFactory != null) sessionFactory.close();
}
}
public
static void main(String args[]) {
new First();
}
}
Из лога видно, что Hibernate создает таблицу, генерирует sql команды insert и select, при этом мы только создаем объект Person и
указываем session что делать с объектом
Person, все просто на
первый взгляд:
2014-08-06 21:21:40,399 DEBUG [org.hibernate.SQL] -
<drop table Persons if exists>
Hibernate: drop table Persons if exists 2014-08-06 21:21:40,400 DEBUG
[org.hibernate.SQL] - <create table Persons (personId bigint generated by
default as identity, fName varchar(255), sName varchar(255), primary key
(personId))>
Hibernate: create table Persons (personId bigint
generated by default as identity, fName varchar(255), sName varchar(255),
primary key (personId))
2014-08-06 21:21:40,470 DEBUG [org.hibernate.SQL] -
<insert into Persons (personId, fName, sName) values (null, ?, ?)>
Hibernate: insert into Persons (personId, fName,
sName) values (null, ?, ?)
Insert Person: Person{id=1, firstName='Vitaly',
secondName='Lopanov'}
2014-08-06 21:21:40,507 DEBUG [org.hibernate.SQL] -
<select person0_.personId as personId0_0_, person0_.fName as fName0_0_,
person0_.sName as sName0_0_ from Persons person0_ where person0_.personId=?>
Hibernate: select person0_.personId as personId0_0_,
person0_.fName as fName0_0_, person0_.sName as sName0_0_ from Persons person0_
where person0_.personId=?
Person select: Person{id=1, firstName='Vitaly',
secondName='Lopanov'}
Комментариев нет:
Отправить комментарий