1. Введение.
1.1. Что такое JDBC?
JDBC (Java Database Connectivity) – это технология, которая обеспечивает доступ Java API к реляционным базам данных. Благодаря этому, наши Java-приложения могут выполнять SQL-запросы и взаимодействовать с базами данных (далее – БД), которые поддерживают SQL.
JDBC является крайне гибкой и позволяет нам писать приложения, которые не зависят от конкретной платформы и могут взаимодействовать с различными СУБД без каких либо изменение в программном коде.
Какие плюсы даёт нам JDBC:
-
Простая и понятная обработка SQL-запросов
-
Крайне удобна для небольших приложений
-
Простой и понятный синтаксис
Какие минусы JDBC:
-
Сложно использовать и поддерживать в больших проектах
-
Большое количество кода
-
Сложно реализовывать концепцию MVC
1.2. Что такое ORM (Object Relational Mapping)?
1.2.1. Проблема
Когда создается приложение на Java и необходимо сделать так, чтобы приложение получило доступ к информации, которая хранится в БД, нужно понимать крайне важную деталь:
Существует огромная разница между объектной и реляционной моделью
СУБД даёт нам информацию в табличном формате, в то время, как Java даёт нам информацию в виде некоего графа объектов.
Представим, что у нас есть класс Developer
, который представляет разработчика:
Developer.java
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private String salary;
public Developer() {
}
public Developer(int id, String firstName, String lastName, String specialty, String salary) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public String getSalary() {
return salary;
}
public void setSalary(String salary) {
this.salary = salary;
}
}
И есть таблица в БД, которая также представляет разработчика:
developer
:CREATE TABLE developer (
developer_id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(50) DEFAULT NULL,
last_name VARCHAR(50) DEFAULT NULL,
specialty VARCHAR(50) DEFAULT NULL,
SALARY INT DEFAULT NULL,
PRIMARY KEY (developer_id)
);
Допустим, что после того, как создан и java-класс и таблица в БД, необходимо изменить БД, тогда сразу же возникает проблема. К тому же, когда записываются или читаются данные в/из БД, тогда есть 5 проблем, которые связаны с разницей между объектно-ориентированной моделью и реляционной моделью:
-
Наследование
В реляционной модели нет никакого понятия, похожего на наследование, которое является одним из ключевых принципов ООП. -
Идентификация
Для БД есть только одна сущность, по которому объект может быть идентифицирован – это Первичный Ключ (Primary Key). В то время как в Java есть такие вещи, как(entity1 == entity2)
и(object1.equals(object2))
. -
Ассоциации
В Java используются ссылки на объекты для ассоциации, а в реляционной модели – Внешний Ключ (Foreign Key). -
Доступ
В Java и в реляционной БД абсолютно разные способы получения доступа к объекту. -
Инкапсуляция
Крайне часто, при разработке приложений, придется столкнуться с тем, что объектно-ориентированная модель имеет больше классов, чем таблиц в БД.
Отсюда возникает проблема: как сделать так, чтобы Java приложение получало доступ к БД и могло корректно интерпретировать эту информацию. Другими словами, нам нужно создать связь между Объектом и реляционной сущностью, иначе говоря Объектно-Реляционное-Связывание или же – ORM (Object-Relational Mapping).
1.2.2. Решение
ORM – это техника программирования, которая служит для того, чтобы обеспечивать преобразование данных при их обмене между реляционной базой данных и Java (ну или других языков программирования).
Так какие же преимущества нам даёт ORM в сравнение с JDBC?
-
Позволяет бизнес-методам обращаться не к БД, а Java-классам
-
Ускоряет разработку приложения
-
Основан на JDBC
-
Отделяет SQL-запросы от объектно-ориентированной модели
-
Позволяет не думать о реализации БД
-
Сущности основаны на бизнес-задачах, а не на структуре БД
-
Управление транзакциями
ORM состоит из:
-
API, который реализует базовые операции (СОЗДАНИЕ, ЧТЕНИЕ, ИЗМЕНЕНИЕ, УДАЛЕНИЕ) объектов-моделей.
-
Средства настройки метаданных связывания
-
Техники взаимодействия с транзакциями, которая позволяет реализовать такие функции, как dirty checking, lazy association fetching и т.д.
А самыми распространёнными ORM фреймворком являются:
-
Hibernate
-
Java Object-Oriented Querying (jOOQ)
-
MyBatis
-
EclipseLink
-
TopLink
2. Архитектура.
Hibernate – это ORM фреймворк для Java с открытым исходным кодом. Эта технология является крайне мощной и имеет высокие показатели производительности.
Hibernate создаёт связь между таблицами в базе данных (далее – БД) и Java-классами и наоборот. Это избавляет разработчиков от огромного количества лишней, рутинной работы, в которой крайне легко допустить ошибку и крайне трудно потом её найти.
Схематично это можно изобразить следующим образом:
Какие же преимущества даёт нам использование Hibernate?
-
Обеспечивает простой API для записи и получения Java-объектов в/из БД.
-
Минимизирует доступ к БД, используя стратегии fetching.
-
Не требует сервера приложения.
-
Позволяет не работать с типами данных языка SQL, а иметь дело с привычными типами данных Java.
-
Заботится о создании связей между Java-классами и таблицами БД с помощью XML-файлов не внося изменения в программный код.
-
Если необходимо изменить БД, то достаточно лишь внести изменения в XML-файлы.
-
Hibernate поддерживает все основные СУБД: MySQL, Oracle, PostgreSQL, Microsoft SQL Server Database, HSQL, DB2.
-
Hibernate также может работать в связке с такими технологиями, как Maven и Jakarta EE.
2.1. Архитектура
Приложение, которе использует Hibernate (в крайне поверхностном представлении) имеет такую архитектуру:
Если рассмотреть строение самого Hibernate более подробно, что этот же рисунок будет выглядеть следующим образом:
Hibernate поддерживает такие API, как:
-
JDBC обеспечивает простейший уровень абстракции функциональности для реляционных БД.
-
JTA позволяют Hibernate использовать серверы приложений Jakarta EE.
-
JNDI позволяют Hibernate использовать серверы приложений Jakarta EE.
2.1.1. Transaction
Этот объект представляет собой рабочую единицу работы с БД. В Hibernate транзакции обрабатываются менеджером транзакций.
2.1.2. SessionFactory
Самый важный и самый тяжёлый объект (обычно создаётся в единственном экземпляре, при запуске приложения). Необходима как минимум одна SessionFactory
для каждой БД, каждый из которых конфигурируется отдельным конфигурационным файлом.
2.1.3. Session
Сессия используется для получения физического соединения с БД. Обычно, сессия создаётся при необходимости, а после этого закрывается. Это связано с тем, что эти объекты крайне легковесны. Чтобы понять, что это такое, можно сказать, что создание, чтение, изменение и удаление объектов происходит через объект Session
.
2.1.4. Query
Этот объект использует HQL или SQL для чтения/записи данных из/в БД. Экземпляр запроса используется для связывания параметров запроса, ограничения количества результатов, которые будут возвращены и для выполнения запроса.
2.1.5. Configuration
Этот объект используется для создания объекта SessionFactory
и конфигурирует сам Hibernate с помощью конфигурационного XML-файла, который объясняет, как обрабатывать объект Session
.
2.1.6. Criteria
Используется для создания и выполнения объектно-ориентированных запросов для получения объектов.
3. ORM (на примере Hibernate)
3.1. Основные понятия JPA и ORM
Java Persistence API (JPA) — спецификация, предоставляет возможность сохранять в удобном виде объекты в базе данных. JPA описывает систему управления сохранением объектов в таблицы реляционных баз данных в удобном виде. JPA реализует концепцию ORM.
ORM — Object-Relational Mapping (объектно-реляционное отображение). Это технология программирования, которая связывает базы данных с концепциями объектно-ориентированных языков программирования. Если упростить, то ORM это связь объектов в Java и записей в базе данных. Самой популярной на данный момент такой системой является Hibernate. Так же к ним можно отнести такие программы как EclipseLink, Toplink, их ещё называют JPA-провайдерами.
Непосредственно JPA представлен в пакете javax.persistence
.
3.2. DataSource
DataSource
— используется для создания соединения с базой данных. Это альтернатива DriverManager
используемого в JDBC
. В документации сказано, что DataSource
использовать предпочтительнее. В частности, одним из преимуществ DataSource
является возможность создания пула соединений Database Connection Pool (DBCP).
Существует много библиотек, которые выступают как DataSource
. Например, такие, как:
-
Hikari – это реализация
DataSource
, которая обеспечивает механизм пула соединений. По сравнению с другими реализациями, он обещает быть легким и более производительным. Является самой популярной библиотекой для пула соединений. -
Tomcat jdbc pool - это реализация
DataSource
из пакетаtomcat-jdbc
. Является достаточно производительной. -
DBCP и C3P0 - устаревшие реализации
DataSource
, являются однопоточными и очень непроизводительными.
3.3. Entity
Entity
- это легковесный хранимый объект бизнес логики (persistent domain object), является основной программной сущностью.
Главная аннотация @Entity
позволяет объектам класса быть связанными с базой данных. Чтобы класс мог быть сущностью, к нему предъявляются следующие требования:
-
Entity
класс должен быть отмечен аннотациейEntity
или описан вXML
файле конфигурации JPA. -
Entity
класс должен содержатьpublic
илиprotected
конструктор без аргументов (он также может иметь конструкторы с аргументами). -
Entity
класс должен быть классом верхнего уровня. -
Entity
класс не может быть enum или интерфейсом. -
Entity
класс не может быть финальным классом. -
Entity
класс не может содержать final поля или методы, если они участвуют в mapping. -
Если объект
Entity
класса будет передаваться по значению как отдельный объект, например через удаленный интерфейс он так же должен реализовыватьSerializable
интерфейс. -
Поля
Entity
класс должны быть напрямую доступны только методам самогоEntity
класса и не должны быть напрямую доступны другим классам, использующим этотEntity
. -
Entity
класс должен содержать первичный ключ, то есть атрибут или группу атрибутов которые уникально определяют запись этогоEntity
класса в базе данных.
3.4. Типы ассоциаций между Entity
Существуют следующие типы ассоциаций:
-
@OneToOne
- используется тогда, когда сущность одной таблицы связанная с одной сущностью другой таблицы. -
@OneToMany
- используется тогда, когда сущность одной таблицы связанная с несколькими сущностями другой таблицы. -
@ManyToOne
- используется тогда, когда сущности одной таблицы связанны с одной сущностью другой таблицы. -
@ManyToMany
- используется тогда, когда сущности одной таблицы связанны с несколькими сущностями другой таблицы.
3.4.1. @OneToOne
Для того чтобы связать сущности отношением один-к-одному, используется аннотация @OneToOne
. В целом, может быть 3 варианта ее использования:
-
Связанные сущности используют одно и то же значение первичного ключа.
-
Внешний ключ определяется полем одной из сущностей (это поле в базе данных должно быть уникальным для имитации отношения один-к-одному).
-
Используется таблица для хранения ссылки между двумя сущностями (ограничение уникальности должно быть установлено на каждом из полей для того, чтобы соответствовать кратности один-к-одному).
Связанные сущности используют одно и то же значение первичного ключа
@Entity
@Table(name = "users")
public class User {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// Поля общие для всех пользователей
}
@Entity
@Table(name = "partners")
public class Partner {
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
private User user;
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// Поля данных партнеров системы
}
Аннотация @PrimaryKeyJoinColumn
указывает на то, что первичный ключ сущности Partner
используется в качестве внешнего ключа для связи с сущностью User
.
Связь один-к-одному с использованием явного внешнего ключа
@Entity
@Table(name = "users")
public class User {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name="passport_id")
private Passport passport;
}
Связь в базе данных между таблицами users
и passports
осуществляется посредством поля passport_id
в таблице users
. Связанное поле в User
объявлено с помощью аннотации @JoinColumn
, ее параметр обозначает поле в базе данных, которое будет использоваться для создания связи.
Связь один-к-одному может быть двунаправленной. В двунаправленных отношениях одна из сторон (и только одна) должна быть владельцем и нести ответственность за обновление связанных полей. В случае когда владельцем выступает сущность User
. Для того чтобы объявить сторону, которая не несет ответственности за отношения, используется атрибут mappedBy
. Он ссылается на имя свойства связи на стороне владельца (passport
).
Passport
@Entity
@Table(name = "passports")
public class Passport {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne(mappedBy = "passport")
private User user;
}
Двунаправленное отношение не создает дополнительного внешнего ключа. Фактически, двунаправленная связь никак не влияет на то, как таблицы связаны друг с другом в базе данных. Просто она позволяет работать с сущностями в обоих направлениях, все также используя единственный внешний ключ. В случае, если на стороне владельца нет связанного поля @JoinColumn
, то выполнятся следующие умолчания: в таблице владельца будет создано поле для связи, имя которого собирается из имени связи на стороне владельца, нижнего подчеркивания и имени уникального ключа на зависящей стороне.
Преимуществом однонаправленной связи, является то, что ею легче управлять, потому что необходимо поддерживать только одну сторону. Преимущество же двунаправленной связи заключается в возможности доступа между связанными сущностями в обоих направлениях. Но обычно это приводит к формированию лишних запросов к базе данных, поэтому использовать двунаправленные связи необходимо осторожно.
Связь один-к-одному с использованием таблицы отношений
User
@Entity
@Table(name = "users")
public class User {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinTable(name = "user_passport",
joinColumns = @JoinColumn(name="user_id"),
inverseJoinColumns = @JoinColumn(name="passport_id")
)
private Passport passport;
}
Passport
@Entity
@Table(name = "passports")
public class Passport {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne(mappedBy = "passport")
private User user;
}
В базе данных таблица users
связана с passports
с помощью таблицы отношений user_passport
. Эта таблица содержит внешний ключ user_id
, указывающий на таблицу users
и внешний ключ passport_id
, указывающий на passports
. @JoinTable
позволяет избежать создания отдельной сущности для таблицы отношений user_passport
, и непосредственно связать сущности User
и Password
между собой. Связь может быть двунаправленной точно так же, как в случае с использованием явного внешнего ключа.
3.4.2. @OneToMany
и @ManyToOne
@OneToMany
— случай, когда у одного автора может быть несколько книг.
Author
@Data
@Entity
@Table(name = "author")
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "book_id", unique = true, nullable = false)
private Set<Book> books;
}
Оно уже является сетом, так как у автора может быть несколько книг. @OneToMany
говорит о типе отношения. FetchType.LAZY
говорит, что не нужно подгружать весь список книг, если это не указанно в запросе.
В классе Book
устанавливаем обратную связь @ManyToOne
:
Book
@Data
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "print_year", nullable = false)
private int printYear;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "author_id", nullable = false)
private Author author;
}
3.4.3. @ManyToMany
Такая зависимость реализовывается через создание дополнительной таблицы. Допустим ситуацию, когда у нескольких книг может быть несколько авторов, а у авторов – несколько книг.
@Data
@ToString(exclude = "books")
@Entity
@Table(name = "author")
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "second_name", nullable = false)
private String secondName;
@ManyToMany
@JoinTable(
name = "author_book_link",
joinColumns = @JoinColumn(name = "author_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "book_id", referencedColumnName = "id")
)
private Set<Book> books;
}
Для связи сущностей создаётся таблица author_book_link
.
@JoinTable
— будет связывать атрибут с дополнительной таблицей author_book_link
. В ней указываются два атрибута, которые будут указывать на primary keys двух сущностей.
Book
@Data
@ToString(exclude = "authors")
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "print_year", nullable = false)
private int printYear;
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "books")
private Set<Author> authors;
}
3.5. Стратегии генерации первичного ключа
Один из главных требований к Entity
является наличие первичного ключа. В JPA на этот случай предусмотрены механизмы автоматической генерации значений суррогатных ключей, которые включаются аннотацией @GeneratedValue
.
JPA поддерживает четыре стратегии генерации ключа:
-
GenerationType.IDENTITY
-
GenerationType.SEQUENCE
-
GenerationType.TABLE
-
GenerationType.AUTO
3.5.1. Стратегия GenerationType.IDENTITY
Такая стратегия работает с базами, у которых есть специальные IDENTITY
поля, например с MySQL или DB2. В таких базах данных возможно создавать первичный ключ с автоматическим инкрементом.
CREATE TABLE journal (
id BIGINT PRIMARY KEY AUTO_INCREMENT
);
3.5.2. Стратегия GenerationType.SEQUENCE
Такая стратегия использует встроенный в базы данных, такие как PostgreSQL или Oracle, механизм генерации последовательных значений. Использование этого генератора требует как создания отдельной sequence в базе данных:
CREATE TABLE journal (
id BIGINT PRIMARY KEY
);
CREATE SEQUENCE book_sequence START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
Так и задания имени этой последовательности в описании ключа:
private class ExampleEntity {
@Id
@SequenceGenerator(name = "bookSequence", sequenceName = "book_sequence")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bookSequence")
@Column(name = "id", nullable = false, updatable = false)
private Long rowId;
}
3.5.3. Стратегия GenerationType.TABLE
Такая стратегия не зависит от поддержки конкретной базой данных и хранит счётчики значений в отдельной таблице. С одной стороны это более гибкое и настраиваемое решение, с другой стороны более медленное и требующее большей настройки. Вначале требуется создать и проинициализировать таблицу для значений ключей:
CREATE TABLE sequence_store (
sequence_name VARCHAR(255) PRIMARY KEY,
sequence_value BIGINT NOT NULL
);
GenerationType.TABLE
private class ExampleEntity {
@Id
@TableGenerator(
name = "seqStore", table = "sequence_store",
pkColumnName = "sequence_name", pkColumnValue = "journal.id.pk",
valueColumnName = "sequence_value", initialValue = 1, allocationSize = 1
)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "seqStore" )
@Column(name = "id", nullable = false, updatable = false)
private Long rowId;
}
3.5.4. Стратегия GenerationType.AUTO
Позволяет автоматически выбрать стратегию в соответствии с используемой базой данных.
3.6. Fetch strategies
В JPA описаны два типа Fetch strategies:
-
LAZY
— данные поля будут загружены только во время первого обращения к этому полю. -
EAGER
— данные поля будут загружены сразу, при инициализации корневой сущности.
Каждый тип связи имеет свою Fetch strategy` по умолчанию:
-
@OneToMany
:LAZY
-
@ManyToOne
:EAGER
-
@ManyToMany
:LAZY
-
@OneToOne
:EAGER
В JPA есть два типа загрузки FetchType
: EAGER
and LAZY
. FetchType.EAGER
загрузка заставляет ORM загружать связанные сущности и коллекции сразу, вместе с корневой сущностью. FetchType.LAZY
загрузка означает, что ORM загрузит сущность или коллекцию отложено, при первом обращении к ней из кода.
FetchType
в JPA говорит когда связанная сущность или коллекция будет загружена. По умолчанию JPA провайдер загружает связанные коллекции (отношения один-ко-многим и многие-ко-многим) отложено. В большинстве случаев отложенная загрузка — оптимальный вариант. Нет смысла инициализировать все связанные коллекции, если к ним не будет обращений.
3.6.1. Каскадные типы
Каскадирование — это стратегия работы со связанными объектами, т.е. когда выполняется какое-либо действие над целевым объектом, то же самое действие будет применено к связанному объекту. Все каскадные операции:
Параметр | Описание |
---|---|
|
При сохранении экземпляра сущности с помощью метода |
|
При удалении экземпляра сущности с помощью метода |
|
При отсоединении экземпляра сущности от контекста хранения с помощью |
|
При слиянии временной или отсоединенной сущности с persistence context с помощью |
|
При изменении экземпляра сущности с помощью |
|
Сокращенная запись для применения всех способов каскадирования. |
3.7. Состояния сущности
Сущности могут находиться в следующих состояниях:
-
transient — объект создан, но при этом ещё не имеет сгенерированных первичных ключей и пока ещё не сохранен в базе данных.
-
managed — объект создан, управляется JPA, имеет сгенерированные первичные ключи.
-
detached — объект был создан, но не управляется (или больше не управляется) JPA.
-
removed — объект создан, управляется JPA, но будет удален после завершения транзакции.
Рассмотрим как операция persist()
на Entity
объекты каждого из четырех статусов:
-
Если статус у сущности transient, то он меняется на managed, и объект будет сохранен в базу при завершении транзакции или в результате
flush()
операций. -
Если статус уже managed, операция игнорируется, однако зависимые
Entity
могут поменять статус на managed, если у них есть аннотации каскадных изменений. -
Если статус removed, то он меняется на managed.
-
Если статус detached, будет выкинут Exception сразу или на этапе завершении транзакции.
Рассмотрим как операция remove()
на Entity
объекты каждого из четырех статусов:
-
Если статус transient, операция игнорируется, однако зависимые
Entity
могут поменять статус на removed, если у них есть аннотации каскадных изменений и они имели статус managed. -
Если статус managed, то статус меняется на removed и запись объект в базе данных будет удалена при завершении транзакции (так же произойдут операции
remove
для всех каскадно зависимых объектов). -
Если статус removed, то операция игнорируется.
-
Если статус detached, будет выкинут Exception сразу или на этапе завершения транзакции.
Рассмотрим как операция merge()
на Entity
объекты каждого из четырех статусов:
-
Если статус у сущности detached, то либо данные будет скопированы в существующей managed
entity
с тем же первичным ключом, либо создан новый managed в который скопируются данные. -
Если статус transient, то будет создана новый managed
entity
, в который будут скопированы данные прошлого объекта. -
Если статус managed, операция игнорируется, однако операция
merge()
сработает на каскадно зависимыеEntity
, если их статус не managed. -
Если статус removed, будет выкинута ошибка сразу или на этапе завершения транзакции.
Рассмотрим как операция refresh()
на Entity
объекты каждого из четырех статусов:
-
Если статус у сущности managed, то в результате операции будут восстановлены все изменения из базы данных данного
Entity
, так же произойдетrefresh()
всех каскадно зависимых объектов. -
Если статус transient, removed или detached, будет выброшен Exception.
Рассмотрим как операция detach()
на Entity
объекты каждого из четырех статусов:
-
Если статус у сущности managed или removed, то в результате операции статус
Entity
(и всех каскадно-зависимых объектов) станет detached. -
Если статус transient или `detached, то операция игнорируется.
3.8. Удаление сирот
Рассмотрим настройку orphanRemoval
, которая касается удаления элементов из коллекции. У нас это будет удаление комментария из списка комментариев топика.
Comment
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String text;
@ManyToOne(fetch = FetchType.LAZY)
private Topic topic;
// getters/setters/constructors
}
Topic
@Entity
public class Topic {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String title;
@OneToMany(mappedBy = "topic", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments=new ArrayList<>();
public void addComment(Comment comment) {
comments.add(comment);
comment.setTopic(this);
}
public void removeComment(Comment comment) {
comments.remove(comment);
comment.setTopic(null);
}
// getters/setters/constructors
}
Следует обратить внимание на метод removeComment()
, он удаляет комментарий из коллекции и устанавливает его полю topic
значение null
.
Чтобы понять смысл настройки orphanRemoval
, надо представить, что теоретически может подразумеваться под удалением комментария из списка комментариев топика. Очевидно это означает, что у данного топика больше нет комментария.
Но остается ли он вообще в базе, то есть можно ли его вывести в общем списке комментариев всех топиков? Или же удаляется из базы? За эти два варианта и отвечает orphanRemoval
.
3.8.1. orphanRemoval
равен true
Если orphanRemoval
равен true
, то при удалении комментария из списка комментариев топика, он удаляется из базы. Проверим это в тесте:
orphanRemoval = true
private class RemoveTests {
@Test
@DisplayName("если orphanRemoval=true, то при удалении комментария из топика он удаляется из базы")
public void givenOrphanRemovalTrue_whenRemoveCommentFromTopic_thenItRemovedFromDatabase() {
Topic topic = topicRepository.getById(-1L);
topic.removeComment(topic.getComments().get(0));
Assertions.assertEquals(2, commentRepository.count());
}
}
Генерируются следующие команды:
select topic0_.id as id1_1_0_, comments1_.id as id1_0_1_,
topic0_.title as title2_1_0_,
comments1_.text as text2_0_1_, comments1_.topic_id as topic_id3_0_1_,
comments1_.topic_id as topic_id3_0_0__, comments1_.id as id1_0_0__
from topic topic0_ inner join comment comments1_
on topic0_.id=comments1_.topic_id
where topic0_.id=?
delete from comment where id=?
Можно заметить оператор delete
, он и удаляет комментарий из базы.
3.8.2. orphanRemoval
равен false
Если orphanRemoval
равен false
, то при удалении комментария из списка, в базе комментарий остается. Его внешний ключ обнуляется, и больше комментарий не ссылается на Topic
.
Проверим это:
orphanRemoval = false
private class RemoveTests {
@Test
@DisplayName("если orphanRemoval = false, то при удалении комментария из топика остается в базе")
public void givenOrphanRemovalFalse_whenRemoveCommentFromTopic_thenItRemovedFromDatabase() {
Topic topic = topicRepository.getById(-1L);
topic.removeComment(topic.getComments().get(0));
Assertions.assertEquals(3, commentRepository.count());
}
}
Генерируются следующие команды:
select topic0_.id as id1_1_0_, comments1_.id as id1_0_1_,
topic0_.title as title2_1_0_, comments1_.text as text2_0_1_,
comments1_.topic_id as topic_id3_0_1_, comments1_.topic_id as topic_id3_0_0__,
comments1_.id as id1_0_0__
from topic topic0_ inner join comment comments1_
on topic0_.id=comments1_.topic_id
where topic0_.id=?
update comment set text=?, topic_id=? where id=?
Здесь происходит обновление таблицы comment
: столбцу topic_id
присваивается значение NULL
. Комментарий остается в базе, просто ни на какой Topic
он больше не ссылается. В свою очередь оператор delete
отсутствует.
4. Конфигурирование.
Для корректной работы, мы должны передать Hibernate подробную информацию, которая связывает наши Java-классы с таблицами в базе данных (далее – БД). Мы, также, должны указать значения определённых свойств Hibernate.
Обычно, вся эта информация помещена в отдельный файл, либо XML-файл – hibernate.cfg.xml
, либо – hibernate.properties
.
В этой статье мы рассмотрим конфигурирование приложение с помощью XML-файла hibernate.cfg.xml
.
Для начала рассмотрим ключевые свойства, которые должны быть настроены в типичном приложении:
-
hibernate.dialect указывает Hibernate диалект БД. Hibernate, в свою очередь, генерирует необходимые SQL-запросы (например,
org.hibernate.dialect.MySQLDialect
, если мы используем MySQL). -
hibernate.connection-driver_class указывает класс JDBC драйвера.
-
hibernate.connection.url указывает URL (ссылку) необходимой нам БД (например,
jdbc:mysql://localhost:3306/database
). -
hibernate.connection.username указывает имя пользователя БД (например,
root
). -
hibernate.connection.password указывает пароль к БД (например,
password
). -
hibernate.connection.pool_size ограничивает количество соединений, которые находятся в пуле соединений Hibernate.
-
hibernate.connection.autocommit указывает режим autocommit для JDBC-соединения.
Давайте рассмотрим пример конфигурационного XML-файла.
Исходные данные:
-
Тип БД: MySQL
-
Имя базы данных:
database
-
Имя пользователя:
root
-
Пароль:
password
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume test is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/database</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">password</property>
</session-factory>
</hibernate-configuration>
5. Сессии.
Session (сессия) используется для получения физического соединения с базой данных (далее – БД). Благодаря тому, что сессия является легковесны объектом, его создают (т.е. открывают сессию) каждый раз, когда возникает необходимость, а потом, когда необходимо, уничтожают (т.е. закрывают сессию). Мы создаём, читаем, редактируем и удаляем объекты с помощью сессий.
Мы стараемся создавать сессии при необходимости, а затем уничтожать их из-за того, что ни не являются потоко-безопасными и не должны быть открыты в течение длительного времени.
Экземпляр класса может находиться в одном из трёх состояний:
-
transient
Это новый экземпляр устойчивого класса, который не привязан к сессии и ещё не представлен в БД. Он не имеет значения, по которому может быть идентифицирован. -
persistent
Мы можем создать переходный экземпляр класса, связав его с сессией. Устойчивый экземпляр класса представлен в БД, а значение идентификатора связано с сессией. -
detached
После того, как сессия закрыта, экземпляр класса становится отдельным, независимым экземпляром класса.
В примитивном виде, транзакция выглядит примерно таким образом:
public class Program {
public static void main(String[] args){
Session session = sessionFactory.openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
/**
* Here we make some work.
* */
transaction.commit();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
e.printStackTrace();
}
} finally {
session.close();
}
}
}
В этом примере, в случае исключения, происходит откат (rollback).
В интерфейсе Session определены 23 метода, которые мы можем использовать:
-
Transaction beginTransaction()
начинает транзакцию и возвращает объект Transaction. -
void cancelQuery()
отменяет выполнение текущего запроса. -
void clear()
полностью очищает сессию -
Connection close()
заканчивает сессию, освобождает JDBC-соединение и выполняет очистку. -
Criteria createCriteria(String entityName)
создание нового экземпляра Criteria для объекта с указанным именем. -
Criteria createCriteria(Class persistentClass)
создание нового экземпляра Criteria для указанного класса. -
Serializable getIdentifier(Object object)
возвращает идентификатор данной сущности, как сущности, связанной с данной сессией. -
void update(String entityName, Object object)
обновляет экземпляр с идентификатором, указанном в аргументе. -
void update(Object object)
обновляет экземпляр с идентификатором, указанном в аргументе. -
void saveOrUpdate(Object object)
сохраняет или обновляет указанный экземпляр. -
Serializable save(Object object)
сохраняет экземпляр, предварительно назначив сгенерированный идентификатор. -
boolean isOpen()
проверяет открыта ли сессия. -
boolean isDirty()
проверят, есть ли в данной сессии какие-либо изменения, которые должны быть синхронизованы с базой данных (далее – БД). -
boolean isConnected()
проверяет, подключена ли сессия в данный момент. -
Transaction getTransaction()
получает связанную с этой сессией транзакцию. -
void refresh(Object object)
обновляет состояние экземпляра из БД. -
SessionFactory getSessionFactory()
возвращает фабрику сессий (SessionFactory), которая создала данную сессию. -
Session get(String entityName, Serializable id)
возвращает сохранённый экземпляр с указанными именем сущности и идентификатором. Если таких сохранённых экземпляров нет – возвращает null. -
void delete(String entityName, Object object)
удаляет сохранённый экземпляр из БД. -
void delete(Object object)
удаляет сохранённый экземпляр из БД. -
SQLQuery createSQLQuery(String queryString)
создаёт новый экземпляр SQL-запроса (SQLQuery) для данной SQL-строки. -
Query createQuery(String queryString)
создаёт новый экземпляр запроса (Query) для данной HQL-строки. -
Query createFilter(Object collection, String queryString)
создаёт новый экземпляр запроса (Query) для данной коллекции и фильтра-строки.
6. Сохраняемые классы.
Ключевая функция Hibernate заключается в том, что можно взять значения из Java-класса и сохранить их в таблице базы данных (далее – БД). С помощью конфигурационных файлов указывают Hibernate как извлечь данные из класса и соединить с определённым столбцами в таблице БД.
Если нужно, чтобы экземпляры (объекты) Java-класса в будущем сохранялись в таблице БД, то их называют “сохраняемые классы” (persistent class). Для того чтобы сделать работу с Hibernate максимально удобной и эффективной, следует использовать программную модель Простых Старых Java Объектов (Plain Old Java Object – POJO).
Существуют определённые требования к POJO классам. Вот самые главные из них:
-
Все классы должны иметь ID для простой идентификации объектов в БД и в Hibernate. Это поле класса соединяется с первичным ключом (primary key) таблицы БД.
-
Все POJO – классы должны иметь конструктор по умолчанию (т.е. без параметров).
-
Все поля POJO – классов должны иметь модификатор доступа
private
иметь набор Getters и Setters в стиле JavaBean. -
POJO – классы не должны содержать бизнес-логику.
Классы POJO называют для того, чтобы подчеркнуть тот факт, что эти объекты являются экземплярами обычных Java-классов.
Developer.java
package net.proselyte.hibernate.pojo;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(int id, String firstName, String lastName, String specialty, int experience) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", specialty='" + specialty + '\'' +
", experience=" + experience +
'}';
}
}
7. Соединяющие файлы.
Чаще всего, когда имеется дело с ORM фреймворком, связи между объектами и таблицами в базе данных (далее – БД) указываются в XML – файле.
POJO – класс Developer.java
package net.proselyte.hibernate.pojo;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(int id, String firstName, String lastName, String specialty, int experience) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", specialty='" + specialty + '\'' +
", experience=" + experience +
'}';
}
}
Теперь создадим таблицу в БД.
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS (
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
На данный момент есть две независимых друг от друга сущности: POJO – класс Developer.java
и таблица в БД HIBERNATE_DEVELOPERS
. Для того чтобы связать их друг с другом и получить возможность сохранять значения полей класса, необходимо объяснить, как именно это делать Hibernate framework. Чтобы это сделать, создаём конфигурационной XML – файл Developer.hbm.xml
:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"https://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.pojo.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer's details.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="specialty" column="last_name" type="string"/>
<property name="experience" column="salary" type="int"/>
</class>
</hibernate-mapping>
На данный момент нельзя сказать, что приложение готово, но есть часть необходимых конфигураций:
-
<hibernate-mapping>
Это ключевой тег, который должен быть в каждом XML – файле для связывания (mapping). Внутри этого тега конфигурируем связи. -
<class>
Тег<class>
используется для того, чтобы указать связь между POJO – классов и таблицей в БД. Имя класса указывается с помощью свойстваname
, имя таблицы в БД – с помощью свойстваtable
. -
<meta>
Опциональный (необязательный) тег, внутри которого можно добавить описание класса. -
<id>
Тег<id>
связывает уникальный идентификатор ID в POJO – классе и первичный ключ (primary key) в таблице БД. Свойствоname
соединяет поле класса со свойствомcolumn
, которое указывает нам колонку в таблице БД. Свойствоtype
определяет тип связывания (mapping) и используется для конвертации типа данных Java в тип данных SQL. -
<generator>
Этот тег внутри тега <id> используется для того, что генерировать первичные ключи автоматически. Если указать это свойствоnative
, как в примере, приведённом выше, то Hibernate сам выберет алгоритм(identity, hilo, sequence)
в зависимости от возможностей БД. -
<property>
Этот тег используется для того, чтобы связать (map) конкретное поле POJO – класса с конкретной колонкой в таблице БД. Свойствоname
указывает поле в классе, в то время как свойствоcolumn
указывает на колонку в таблице БД. Свойствоtype
указывает тип связывания (mapping) и конвертирует тип данных Java в тип данных SQL.
Существуют также и другие теги, которые могут быть использованы в конфигурационном XML – файле и не упоминались до этого времени.
8. Пример простого приложения.
8.1. Шаг 1. Создать POJO – класс Developer.java
Developer.java
package net.proselyte.hibernate.example;
public class Developer {
protected int id;
protected String firstName;
protected String lastName;
protected String specialty;
protected int experience;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(int id, String firstName, String lastName, String specialty, int experience) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", specialty='" + specialty + '\'' +
", experience=" + experience +
'}';
}
}
8.2. Шаг 2. Создание таблицы в базе данных (далее – БД).
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
8.3. Шаг 3. создание конфигурационного файла hibernate.cfg.xml
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume ИМЯ ВАШЕЙ БД is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БАЗЫ_ДАННЫХ</property>
<property name="hibernate.connection.username">ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Четвёртый шаг – создание конфигурационного XML – файла Developer.hbm.xml
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.example.model.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer's details.
</meta>
<id name="id" type="int" column="ID">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
</hibernate-mapping>
И финальный шаг – создание основного класса приложения DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.example;
import net.proselyte.hibernate.example.model.Developer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.List;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Adding developer's records to the DB");
/**
* Adding developer's records to the database (DB)
*/
developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2);
developerRunner.addDeveloper("Some", "Developer", "C++ Developer", 2);
developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4);
System.out.println("List of developers");
/**
* List developers
*/
List developers = developerRunner.listDevelopers();
for (Developer developer : developers) {
System.out.println(developer);
}
System.out.println("===================================");
System.out.println("Removing Some Developer and updating Proselyte");
/**
* Update and Remove developers
*/
developerRunner.updateDeveloper(10, 3);
developerRunner.removeDeveloper(11);
System.out.println("Final list of developers");
/**
* List developers
*/
developers = developerRunner.listDevelopers();
for (Developer developer : developers) {
System.out.println(developer);
}
System.out.println("===================================");
}
public void addDeveloper(String firstName, String lastName, String specialty, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
session.save(developer);
transaction.commit();
session.close();
}
public List listDevelopers() {
Session session = this.sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
transaction.commit();
session.close();
return developers;
}
public void updateDeveloper(int developerId, int experience) {
Session session = this.sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = this.sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если все настройки были сделаны правильно, то получаем следующий результат работы программы:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7538 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.0.7.Final/hibernate-core-5.0.7.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.example.DeveloperRunner Feb 18, 2016 2:30:00 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.0.7.Final} Feb 18, 2016 2:30:00 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 18, 2016 2:30:00 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 18, 2016 2:30:00 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 18, 2016 2:30:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 18, 2016 2:30:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 18, 2016 2:30:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 18, 2016 2:30:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 18, 2016 2:30:02 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Thu Feb 18 14:30:02 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 18, 2016 2:30:02 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding developer's records to the DB List of developers Feb 18, 2016 2:30:03 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 10 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Developer: id: 11 First Name: Some Last Name: Developer Specialty: C++ Developer Experience: 2 Developer: id: 12 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 =================================== Removing Some Developer and updating Proselyte Final list of developers Developer: id: 10 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Developer: id: 12 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 ===================================
9. Виды связей. List Mapping.
Коллекция Java, которая хранит последовательность элементов и в которой допускается хранение одинаковых элементов называется List. Получить доступ к любому элементу List
можно по индексу.
List
связывается (mapped) с помощью элемента <list>
и инициализируется с помощью java.util.ArrayList
.
9.1. Шаг 1. Создаём 2 таблицы в базе данных (далее – БД)
CREATE TABLE HIBERNATE_DEVELOPERS( ID INT NOT NULL AUTO_INCREMENT, FIRST_NAME VARCHAR(50) DEFAULT NULL, LAST_NAME VARCHAR(50) DEFAULT NULL, SPECIALTY VARCHAR(50) DEFAULT NULL, EXPERIENCE INT DEFAULT NULL, PRIMARY KEY(ID) );
HIBERNATE_PROJECTS
create table HIBERNATE_PROJECTS ( id INT NOT NULL auto_increment, PROJECT_NAME VARCHAR(50) default NULL, COMPANY VARCHAR(50) default NULL, idx INT default NULL, DEVELOPER_ID INT default NULL, PRIMARY KEY (id) );
9.2. Шаг 2. Создаём POJO-классы
Developer.java
package net.proselyte.hibernate.mappings.list;
import java.util.List;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private List projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public List getProjects() {
return projects;
}
public void setProjects(List projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
Project.java
package net.proselyte.hibernate.mappings.list;
public class Project {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
9.3. Шаг 3. Создаём конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost/ИМЯ ВАШЕЙ БД
</property>
<property name="hibernate.connection.username">
ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ
</property>
<property name="hibernate.connection.password">
ВАШ ПАРОЛЬ
</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.mappings.list.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer's details.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<list name="projects" cascade="all">
<key column="DEVELOPER_ID"/>
<list-index column="idx"/>
<one-to-many class="net.proselyte.hibernate.mappings.list.Project"/>
</list>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.mappings.list.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">
This class contains project's records.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
9.4. Шаг 4. Создаём гласс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.mappings.list;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.ArrayList;
import java.util.List;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the set of projects.");
ArrayList projects1 = new ArrayList();
projects1.add(new Project("Proselyte Tutorial", "proselyte.net"));
projects1.add(new Project("SkybleLib", "SkybleSoft"));
ArrayList projects2 = new ArrayList();
projects2.add(new Project("Some Project", "Some Company"));
projects2.add(new Project("One more Project", "One more Company"));
System.out.println("Adding developer's records to the DB");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
Integer developerId2 = developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(developerId1, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, ArrayList projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
List projects = developer.getProjects();
for (Project project : projects) {
System.out.println(project);
}
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
9.5. Результат работы программы:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7532 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.0.7.Final/hibernate-core-5.0.7.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.mappings.list.DeveloperRunner Feb 20, 2016 6:22:36 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.0.7.Final} Feb 20, 2016 6:22:36 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 20, 2016 6:22:36 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 20, 2016 6:22:36 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 20, 2016 6:22:38 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 20, 2016 6:22:38 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 20, 2016 6:22:38 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 20, 2016 6:22:38 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 20, 2016 6:22:38 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Sat Feb 20 18:22:38 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 20, 2016 6:22:39 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Creating the set of projects. Adding developer's records to the DB List of developers Feb 20, 2016 6:22:40 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 63 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Project: id: 13 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 14 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 64 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 15 Project Name: Some Project Company Name: Some Company Project: id: 16 Project Name: One more Project Company Name: One more Company ================ =================================== Updating Proselyte Final list of developers Developer: id: 63 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Project: id: 13 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 14 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 64 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 15 Project Name: Some Project Company Name: Some Company Project: id: 16 Project Name: One more Project Company Name: One more Company ================ =================================== Feb 20, 2016 6:22:40 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
В этой статье рассмотрены основы связывания (mapping) с использованием коллекции List
.
10. Виды связей. Collection Mapping
Коллекция Java, которая хранит элементы без какой-либо последовательности и позволяет хранение одинаковых элементов, называется Bag.
Collection
связывается (mapped) с помощью элемента <bag>
и инициализируется с помощью java.util.ArrayList
.
Пример:
10.1. Шаг 1. Создадим две табилцы в базе данных (далее – БД)
CREATE TABLE HIBERNATE_DEVELOPERS( ID INT NOT NULL AUTO_INCREMENT, FIRST_NAME VARCHAR(50) DEFAULT NULL, LAST_NAME VARCHAR(50) DEFAULT NULL, SPECIALTY VARCHAR(50) DEFAULT NULL, EXPERIENCE INT DEFAULT NULL, PRIMARY KEY(ID) );
HIBERNATE_PROJECTS
create table HIBERNATE_PROJECTS ( id INT NOT NULL auto_increment, PROJECT_NAME VARCHAR(50) default NULL, COMPANY VARCHAR(50) default NULL, DEVELOPER_ID INT default NULL, PRIMARY KEY (id) );
10.2. Шаг 2. Создадим POJO – классы
Developer.java
package net.proselyte.hibernate.mappings.list;
import java.util.Collection;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private Collection projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public Collection getProjects() {
return projects;
}
public void setProjects(Collection projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
Project.java
package net.proselyte.hibernate.mappings.list;
public class Project {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
10.3. Шаг 3. Создадим конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost/ИМЯ ВАШЕЙ БАЗЫ ДАННЫХ
</property>
<property name="hibernate.connection.username">
ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ
</property>
<property name="hibernate.connection.password">
ВАШ ПАРОЛЬ
</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Deveoper.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.mappings.list.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer's details.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<bag name="projects" cascade="all">
<key column="DEVELOPER_ID"/>
<one-to-many class="net.proselyte.hibernate.mappings.list.Project"/>
</bag>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.mappings.list.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">
This class contains project's records.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
10.4. Шаг 4. Создадим класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.mappings.list;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.ArrayList;
import java.util.Collection;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the collection of projects.");
ArrayList projects1 = new ArrayList();
projects1.add(new Project("Proselyte Tutorial", "proselyte.net"));
projects1.add(new Project("SkybleLib", "SkybleSoft"));
ArrayList projects2 = new ArrayList();
projects2.add(new Project("Some Project", "Some Company"));
projects2.add(new Project("One more Project", "One more Company"));
System.out.println("Adding developer's records to the DB");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
Integer developerId2 = developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(developerId1, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, ArrayList projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Collection developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
Collection projects = developer.getProjects();
for (Project project : projects) {
System.out.println(project);
}
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
10.5. Результат работы программы:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7533 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.0.7.Final/hibernate-core-5.0.7.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.mappings.list.DeveloperRunner Feb 20, 2016 7:45:08 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.0.7.Final} Feb 20, 2016 7:45:08 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 20, 2016 7:45:08 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 20, 2016 7:45:09 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 20, 2016 7:45:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 20, 2016 7:45:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 20, 2016 7:45:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 20, 2016 7:45:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 20, 2016 7:45:10 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Sat Feb 20 19:45:10 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 20, 2016 7:45:11 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Creating the collection of projects. Adding developer's records to the DB List of developers Feb 20, 2016 7:45:11 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 67 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Project: id: 5 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 6 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 68 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 7 Project Name: Some Project Company Name: Some Company Project: id: 8 Project Name: One more Project Company Name: One more Company ================ =================================== Updating Proselyte Final list of developers Developer: id: 67 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Project: id: 5 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 6 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 68 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 7 Project Name: Some Project Company Name: Some Company Project: id: 8 Project Name: One more Project Company Name: One more Company ================ =================================== Feb 20, 2016 7:45:11 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
В этой статье рассмотрен пример связывания с использованием Collection
.
11. Виды связей. Set Mapping.
Set
(множество) – это коллекция, которая не содержит повторяющихся элементов (только уникальные).
Для обеспечения уникальности значений, объекты, содержащиеся в Set
, должны реализовывать метода equals()
и hashCode()
,
используя которые, Java-приложение сможет определить являются 2 элемента одинаковыми или нет.
Set
связывается (mapped) с помощью элемента <set>
и инициализируется с помощью java.util.HashSet
.
Другими словами, используем Set
, если в коллекции будут храниться только уникальные элементы.
Для понимания, как это реализовывается на практике, рассмотрим пример небольшого приложения.
11.1. Шаг 1. Создадим две таблицы в нашей БД:
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS (
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
HIBERNATE_PROJECTS
CREATE TABLE HIBERNATE_PROJECTS (
id INT NOT NULL AUTO_INCREMENT,
PROJECT_NAME VARCHAR(50) default NULL,
COMPANY VARCHAR(50) default NULL,
DEVELOPER_ID INT default NULL,
PRIMARY KEY (id)
);
Будем использовать отношение one-to-many между таблицами HIBERNATE_DEVELOPERS
и HIBERNATE_PROJECTS
.
11.2. Шаг 2. Создаём класс Developer.java
Developer.java
package net.proselyte.hibernate.mappings.set;
import java.util.Set;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private Set projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public Set getProjects() {
return projects;
}
public void setProjects(Set projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
11.3. Шаг 3. Создаём класс Project.java
Project.java
package net.proselyte.hibernate.mappings.set;
public class Project {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
/**
* Methods equals and hashCode for comparing objects
*/
public boolean equals(Object object) {
if (object == null) {
return false;
}
if (!this.getClass().equals(object.getClass())) {
return false;
}
Project object2 = (Project) object;
if ((this.id == object2.getId()) && (this.projectName == object2.getProjectName()) && (this.companyName == object2.getCompanyName())) {
return true;
}
return false;
}
public int hasCode() {
int code = 0;
code = (id + projectName + companyName).hashCode();
return code;
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
11.4. Шаг 4. Создаём конфигурационный файл hibernate.cfg.xml
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume ИМЯ ВАШЕЙ БД is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/PROSELYTE_TUTORIAL</property>
<property name="hibernate.connection.username">ИМЯ ВАШЕЙ БАЗЫ ДАННЫХ</property>
<property name="hibernate.connection.password">ВАШ ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
11.5. Шаг 5. Создаём конфигурационный файл Developer.hbm.xml
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.mappings.set.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains the developer's detail.</meta>
<id name="id" type="int" column="ID">
<generator class="native"/>
</id>
<set name="projects" cascade="all">
<key column="DEVELOPER_ID"/>
<one-to-many class="net.proselyte.hibernate.mappings.set.Project"/>
</set>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.mappings.set.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">This class contains the projects records.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
11.6. Шаг 6. Создаём и запускаем класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.mappings.set;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the set of projects.");
HashSet projects1 = new HashSet();
projects1.add(new Project("Proselyte Tutorial", "proselyte.net"));
projects1.add(new Project("SkybleLib", "SkybleSoft"));
HashSet projects2 = new HashSet();
projects2.add(new Project("Some Project", "Some Company"));
projects2.add(new Project("One more Project", "One more Company"));
System.out.println("Adding developer's records to the DB");
developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(31, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public void addDeveloper(String firstName, String lastName, String specialty, int experience, Set projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
session.save(developer);
transaction.commit();
session.close();
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
Set projects = developer.getProjects();
for (Project project : projects) {
System.out.println(project);
}
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано верно, то в результате получим следующее:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7545 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.0.7.Final/hibernate-core-5.0.7.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.mappings.set.DeveloperRunner Feb 18, 2016 9:33:06 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.0.7.Final} Feb 18, 2016 9:33:06 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 18, 2016 9:33:06 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 18, 2016 9:33:06 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 18, 2016 9:33:07 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 18, 2016 9:33:07 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 18, 2016 9:33:07 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 18, 2016 9:33:07 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 18, 2016 9:33:07 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Thu Feb 18 21:33:08 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 18, 2016 9:33:08 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Creating the set of projects. Adding developer's records to the DB List of developers Feb 18, 2016 9:33:09 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 29 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Project: id: 33 Project Name: SkybleLib Company Name: SkybleSoft Project: id: 34 Project Name: Proselyte Tutorial Company Name: proselyte.net ================ Developer: id: 30 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 35 Project Name: Some Project Company Name: Some Company Project: id: 36 Project Name: One more Project Company Name: One more Company ================ Developer: id: 31 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Project: id: 37 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 38 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 32 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 40 Project Name: One more Project Company Name: One more Company Project: id: 39 Project Name: Some Project Company Name: Some Company ================ =================================== Updating Proselyte Final list of developers Developer: id: 29 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Project: id: 33 Project Name: SkybleLib Company Name: SkybleSoft Project: id: 34 Project Name: Proselyte Tutorial Company Name: proselyte.net ================ Developer: id: 30 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 35 Project Name: Some Project Company Name: Some Company Project: id: 36 Project Name: One more Project Company Name: One more Company ================ Developer: id: 31 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Project: id: 37 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 38 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 32 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 39 Project Name: Some Project Company Name: Some Company Project: id: 40 Project Name: One more Project Company Name: One more Company ================ =================================== Feb 18, 2016 9:33:09 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
12. Виды связей. SortedSet Mapping.
SortedSet
– это коллекция, которая содержит только уникальные элементы и эти элементы упорядочены в естественном порядке или с помощью специального класса-компаратора.
SortedSet
связывается (mapped) с помощью элемента <set>
в таблице связей и инициализируется с помощью java.util.TreeSet
.
Свойство sort
определяет, будет ли происходить сортировка в натуральном порядке или с помощью класса-компаратора, созданного разработчиком.
Если мы выбираем натуральный порядок, то элементы будут упорядочены в восходящем порядке.
Другими словами, мы используем SortedSet
, если в коллекции будут храниться только уникальные элементы и нам необходимо их определённым образом упорядочить.
Для понимания, как это реализовывается на практике, рассмотрим пример небольшого приложения.
12.1. Шаг 1. Создадим две таблицы в нашей БД:
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
HIBERNATE_PROJECTS
create table HIBERNATE_PROJECTS (
id INT NOT NULL AUTO_INCREMENT,
PROJECT_NAME VARCHAR(50) default NULL,
COMPANY VARCHAR(50) default NULL,
DEVELOPER_ID INT default NULL,
PRIMARY KEY (id)
);
Мы будем использовать отношение one-to-many между таблицами HIBERNATE_DEVELOPERS
и HIBERNATE_PROJECTS
.
12.2. Шаг 2. Создаём класс Developer.java
Developer.java
package net.proselyte.hibernate.mappings.sortedset;
import java.util.SortedSet;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private SortedSet projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public SortedSet getProjects() {
return projects;
}
public void setProjects(SortedSet projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
12.3. Шаг 3. Создём класс Project.java
Project.java
package net.proselyte.hibernate.mappings.sortedset;
public class Project implements Comparable {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public int compareTo(Project that) {
final int BEFORE = -1;
final int AFTER = 1;
if (that == null) {
return BEFORE;
}
Comparable thisProject = this.getProjectName();
Comparable thatProject = that.getProjectName();
if (thisProject == null) {
return AFTER;
} else if (thatProject == null) {
return BEFORE;
} else {
return thisProject.compareTo(thatProject);
}
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
12.4. Шаг 4. Создаём класс-компаратор ProjectsComparator.java
ProjectsComparator.java
package net.proselyte.hibernate.mappings.sortedset;
import java.util.Comparator;
public class ProjectsComparator implements Comparator {
@Override
public int compare(Project o1, Project o2) {
final int BEFORE = -1;
final int AFTER = 1;
if (o2 == null) {
return BEFORE * -1;
}
Comparable thisProject = o1.getProjectName();
Comparable thatProject = o2.getProjectName();
if (thisProject == null) {
return AFTER * 1;
} else if (thatProject == null) {
return BEFORE * -1;
} else {
return thisProject.compareTo(thatProject) * -1;
}
}
}
12.5. Шаг 5. Создаём конфигурационный файл hibernate.cfg.xml
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ ВАШЕЙ БАЗЫ ДАННЫХ</property>
<property name="hibernate.connection.username">ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
12.6. Шаг 6. Создаём конфигурационный файл Developer.hbm.xml
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.mappings.sortedset.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains the developer's detail.</meta>
<id name="id" type="int" column="ID">
<generator class="native"/>
</id>
<set name="projects" cascade="all" sort="net.proselyte.hibernate.mappings.sortedset.ProjectsComparator">
<key column="DEVELOPER_ID"/>
<one-to-many class="net.proselyte.hibernate.mappings.sortedset.Project"/>
</set>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.mappings.sortedset.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">This class contains the projects records.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
12.7. Шаг 7. Создаём и запускаем класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.mappings.sortedset;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the set of projects.");
TreeSet projects1 = new TreeSet();
projects1.add(new Project("Proselyte Tutorial", "proselyte.net"));
projects1.add(new Project("SkybleLib", "SkybleSoft"));
TreeSet projects2 = new TreeSet();
projects2.add(new Project("Some Project", "Some Company"));
projects2.add(new Project("One more Project", "One more Company"));
System.out.println("Adding developer's records to the DB");
developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(35, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public void addDeveloper(String firstName, String lastName, String specialty, int experience, SortedSet projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
session.save(developer);
transaction.commit();
session.close();
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
SortedSet projects = developer.getProjects();
for (Project project : projects) {
System.out.println(project);
}
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано верно, то в результате мы получим следующее:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7532 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.0.7.Final/hibernate-core-5.0.7.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.mappings.sortedset.DeveloperRunner Feb 18, 2016 10:01:13 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.0.7.Final} Feb 18, 2016 10:01:13 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 18, 2016 10:01:13 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 18, 2016 10:01:13 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 18, 2016 10:01:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 18, 2016 10:01:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 18, 2016 10:01:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 18, 2016 10:01:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 18, 2016 10:01:15 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Thu Feb 18 22:01:15 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 18, 2016 10:01:15 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Creating the set of projects. Adding developer's records to the DB List of developers Feb 18, 2016 10:01:16 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 35 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Project: id: 46 Project Name: SkybleLib Company Name: SkybleSoft Project: id: 45 Project Name: Proselyte Tutorial Company Name: proselyte.net ================ Developer: id: 36 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 48 Project Name: Some Project Company Name: Some Company Project: id: 47 Project Name: One more Project Company Name: One more Company ================ =================================== Updating Proselyte Final list of developers Developer: id: 35 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Project: id: 46 Project Name: SkybleLib Company Name: SkybleSoft Project: id: 45 Project Name: Proselyte Tutorial Company Name: proselyte.net ================ Developer: id: 36 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 48 Project Name: Some Project Company Name: Some Company Project: id: 47 Project Name: One more Project Company Name: One more Company ================ =================================== Feb 18, 2016 10:01:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
13. Виды связей. Map Mapping.
Коллекция Java, хранящая пары “ключ – значение” и в которой допускается хранение только уникальных элементов, называется Map
. Map
связывается (mapped) с помощью элемента <map>
и (в случае неупорядоченной Map
) инициализируется с помощью java.util.HashMap
.
Для понимания того, как это работает на практике, рассмотрим пример простого приложения.
13.1. Шаг 1. Создадим две таблицы в бахе данных (далее – БД)
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS (
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
HIBERNATE_PROJECTS
create table HIBERNATE_PROJECTS (
id INT NOT NULL auto_increment,
SPHERE VARCHAR(50) default NULL,
PROJECT_NAME VARCHAR(50) default NULL,
COMPANY VARCHAR(50) default NULL,
DEVELOPER_ID INT default NULL,
PRIMARY KEY (id)
);
Мы будем использовать отношение one-to-many между таблицами HIBERNATE_DEVELOPERS
и HIBERNATE_PROJECTS
.
13.2. Шаг 2. Создадим POJO – классы
Developer.java
package net.proselyte.hibernate.mappings.map;
import java.util.Map;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private Map<String, Project> projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public Map<String, Project> getProjects() {
return projects;
}
public void setProjects(Map<String, Project> projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
Project.java
package net.proselyte.hibernate.mappings.map;
public class Project {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
13.3. Шаг 3. Создадим конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ ВАШЕЙ БД</property>
<property name="hibernate.connection.username">ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.mappings.map.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains developer's details.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<map name="projects" cascade="all">
<key column="DEVELOPER_ID"/>
<index column="SPHERE" type="string" />
<one-to-many class="net.proselyte.hibernate.mappings.map.Project"/>
</map>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.mappings.map.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">This class contains project's records.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
13.4. Шаг 4. Создаём класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.mappings.map;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the collection of projects.");
HashMap<String, Project> projects1 = new HashMap<String, Project>();
projects1.put("Computer Science", new Project("Proselyte Tutorial", "proselyte.net"));
projects1.put("Aviation", new Project("SkybleLib", "SkybleSoft"));
HashMap<String, Project> projects2 = new HashMap<String, Project>();
projects2.put("Some Sphere", new Project("Some Project", "Some Company"));
projects2.put("E-commerce", new Project("One more Project", "One more Company"));
System.out.println("Adding developer's records to the DB");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
Integer developerId2 = developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(developerId1, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, HashMap<String, Project> projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
Map<String, Project> projects = developer.getProjects();
System.out.println(developer.getFirstName() + "'s projects:\n");
System.out.println(projects);
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, то в результате работы программы мы получим, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7540 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.0.7.Final/hibernate-core-5.0.7.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.mappings.map.DeveloperRunner Feb 20, 2016 8:30:38 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.0.7.Final} Feb 20, 2016 8:30:38 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 20, 2016 8:30:38 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 20, 2016 8:30:38 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 20, 2016 8:30:40 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 20, 2016 8:30:40 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 20, 2016 8:30:40 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 20, 2016 8:30:40 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 20, 2016 8:30:40 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Sat Feb 20 20:30:40 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 20, 2016 8:30:40 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Creating the collection of projects. Adding developer's records to the DB List of developers Feb 20, 2016 8:30:41 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 79 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Proselyte's projects: {Aviation=Project: id: 21 Project Name: SkybleLib Company Name: SkybleSoft , Computer Science=Project: id: 22 Project Name: Proselyte Tutorial Company Name: proselyte.net } ================ Developer: id: 80 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Peter's projects: {Some Sphere=Project: id: 23 Project Name: Some Project Company Name: Some Company , E-commerce=Project: id: 24 Project Name: One more Project Company Name: One more Company } ================ =================================== Updating Proselyte Final list of developers Developer: id: 79 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Proselyte's projects: {Aviation=Project: id: 21 Project Name: SkybleLib Company Name: SkybleSoft , Computer Science=Project: id: 22 Project Name: Proselyte Tutorial Company Name: proselyte.net } ================ Developer: id: 80 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Peter's projects: {Some Sphere=Project: id: 23 Project Name: Some Project Company Name: Some Company , E-commerce=Project: id: 24 Project Name: One more Project Company Name: One more Company } ================ =================================== Feb 20, 2016 8:30:41 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
14. Виды связей. SortedMap Mapping.
Коллекция Java, хранящая только уникальные (разрешено хранение только уникальных элементов) пары “ключ – значение” и в которой все элементы упорядочены, называется SortedMap
.
SortedMap
связывается (mapped) с помощью элемента <map>
и инициализируется с помощью java.util.TreeMap
.
Для понимания того, как это работает на практике, рассмотрим пример простого приложения.
14.1. Шаг 1. Создадим две таблицы в бахе данных (далее – БД)
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS (
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
HIBERNATE_PROJECTS
create table HIBERNATE_PROJECTS (
id INT NOT NULL auto_increment,
SPHERE VARCHAR(50) default NULL,
PROJECT_NAME VARCHAR(50) default NULL,
COMPANY VARCHAR(50) default NULL,
DEVELOPER_ID INT default NULL,
PRIMARY KEY (id)
);
Мы будем использовать отношение one-to-many между таблицами HIBERNATE_DEVELOPERS
и HIBERNATE_PROJECTS
.
14.2. Шаг 2. Создадим POJO – классы
Developer.java
package net.proselyte.hibernate.mappings.sortedmap;
import java.util.SortedMap;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private SortedMap<String, Project> projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public SortedMap<String, Project> getProjects() {
return projects;
}
public void setProjects(SortedMap<String, Project> projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
Project.java
package net.proselyte.hibernate.mappings.sortedmap;
public class Project implements Comparable {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public int compareTo(String that) {
final int BEFORE = -1;
final int AFTER = 1;
if (that == null) {
return BEFORE;
}
Comparable thisProject = this;
Comparable thatProject = that;
if (thisProject == null) {
return AFTER;
} else if (thatProject == null) {
return BEFORE;
} else {
return thisProject.compareTo(thatProject);
}
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
ProjectComparator.java
package net.proselyte.hibernate.mappings.sortedmap;
import java.util.Comparator;
public class ProjectComparator implements Comparator {
public int compare(String o1, String o2) {
final int BEFORE = -1;
final int AFTER = 1;
/* To reverse the sorting order, multiple by -1 */
if (o2 == null) {
return BEFORE * -1;
}
Comparable thisProject = o1;
Comparable thatProject = o2;
if (thisProject == null) {
return AFTER * 1;
} else if (thatProject == null) {
return BEFORE * -1;
} else {
return thisProject.compareTo(thatProject) * -1;
}
}
}
14.3. Шаг 3. Создадим конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ ВАШЕЙ БД</property>
<property name="hibernate.connection.username">ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.mappings.sortedmap.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains developer's details.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<map name="projects" cascade="all" sort = "net.proselyte.hibernate.mappings.sortedmap.ProjectComparator">
<key column="DEVELOPER_ID"/>
<index column="SPHERE" type="string" />
<one-to-many class="net.proselyte.hibernate.mappings.sortedmap.Project"/>
</map>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.mappings.sortedmap.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">This class contains project's records.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
14.4. Шаг 4. Создаём класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.mappings.sortedmap;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the TreeMaps of projects.");
TreeMap<String, Project> projects1 = new TreeMap<String, Project>();
projects1.put("Computer Science", new Project("Proselyte Tutorial", "proselyte.net"));
projects1.put("Aviation", new Project("SkybleLib", "SkybleSoft"));
TreeMap<String, Project> projects2 = new TreeMap<String, Project>();
projects2.put("Some Sphere", new Project("Some Project", "Some Company"));
projects2.put("E-commerce", new Project("One more Project", "One more Company"));
System.out.println("Adding developer's records to the DB");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
Integer developerId2 = developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(developerId1, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, TreeMap<String, Project> projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
SortedMap<String, Project> projects = developer.getProjects();
System.out.println(developer.getFirstName() + "'s projects:\n");
System.out.println(projects);
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, то в результате работы программы мы получим, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7534 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/3.6.9.Final/hibernate-core-3.6.9.Final.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.6/antlr-2.7.6.jar:/home/proselyte/.m2/repository/commons-collections/commons-collections/3.1/commons-collections-3.1.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-commons-annotations/3.2.0.Final/hibernate-commons-annotations-3.2.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.0-api/1.0.1.Final/hibernate-jpa-2.0-api-1.0.1.Final.jar:/home/proselyte/.m2/repository/javax/transaction/jta/1.1/jta-1.1.jar:/home/proselyte/.m2/repository/org/slf4j/slf4j-api/1.6.1/slf4j-api-1.6.1.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.mappings.sortedmap.DeveloperRunner SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Sat Feb 20 21:45:50 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Creating the TreeMaps of projects. Adding developer's records to the DB List of developers Developer: id: 81 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Proselyte's projects: {Computer Science=Project: id: 26 Project Name: Proselyte Tutorial Company Name: proselyte.net , Aviation=Project: id: 25 Project Name: SkybleLib Company Name: SkybleSoft } ================ Developer: id: 82 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Peter's projects: {Some Sphere=Project: id: 28 Project Name: Some Project Company Name: Some Company , E-commerce=Project: id: 27 Project Name: One more Project Company Name: One more Company } ================ =================================== Updating Proselyte Final list of developers Developer: id: 81 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Proselyte's projects: {Computer Science=Project: id: 26 Project Name: Proselyte Tutorial Company Name: proselyte.net , Aviation=Project: id: 25 Project Name: SkybleLib Company Name: SkybleSoft } ================ Developer: id: 82 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Peter's projects: {Some Sphere=Project: id: 28 Project Name: Some Project Company Name: Some Company , E-commerce=Project: id: 27 Project Name: One more Project Company Name: One more Company } ================ ===================================
15. Виды связей. Many-to-One.
Наиболее распространённым типом ассоциации является тип Many-to-One, при котором один объект может быть ассоциирован с несколькими другими объектами. Например, мы можем ассоциировать несколько разработчиков с одной компанией, в которой они работают.
Для понимания того, как это работает на практике, рассмотрим пример простого приложения.
15.1. Шаг 1. Создадим две таблицы в нашей базе данных (далее – БД)
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
COMPANY INT NOT NULL,
PRIMARY KEY(ID)
);
HIBERNATE_COMPANIES
CREATE TABLE HIBERNATE_COMPANIES (
id INT NOT NULL AUTO_INCREMENT,
COMPANY_NAME VARCHAR(100) default NULL,
PRIMARY KEY (id)
);
15.2. Шаг 2. Создадим POJO – классы
Developer.java
package net.proselyte.hibernate.association.manytoone;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private Company company;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience, Company company) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
this.company = company;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n" +
"Company: " + company;
}
}
Company.java
package net.proselyte.hibernate.association.manytoone;
public class Company {
private int id;
private String companyName;
public Company() {
}
public Company(String companyName) {
this.companyName = companyName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
public String toString() {
return "Company:" +
"\nCompany ID: " + id +
"\nCompany Name: " + companyName + "\n";
}
}
15.3. Шаг 3. Создадим конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БД
</property>
<property name="hibernate.connection.username">
ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ
</property>
<property name="hibernate.connection.password">
ВАШ_ПАРОЛЬ
</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.association.manytoone.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer's detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
<many-to-one name="company" column="COMPANY" not-null="true"
class="net.proselyte.hibernate.association.manytoone.Company"/>
</class>
<class name="net.proselyte.hibernate.association.manytoone.Company" table="HIBERNATE_COMPANIES">
<meta attribute="class-description">
This class contains company details.
</meta>
<id name="id" type="int" column="ID">
<generator class="native"/>
</id>
<property name="companyName" column="COMPANY_NAME" type="string"/>
</class>
</hibernate-mapping>
15.4. Шаг 4. Создадим класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.association.manytoone;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.List;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Adding company record...");
Company company = developerRunner.addCompany("Proselyte.net");
System.out.println("Creating developer's records...");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, company);
Integer developerId2 = developerRunner.addDeveloper("Peter", "Programmer", "C++ Developer", 2, company);
System.out.println("List of Developers: ");
developerRunner.listDevelopers();
System.out.println("Updating experience of Proselyte to 3 years and removing Peter...");
developerRunner.updateDeveloper(developerId1, 3);
developerRunner.removeDeveloper(developerId2);
System.out.println("Final list of Developers: ");
developerRunner.listDevelopers();
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, Company company) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience, company);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public Company addCompany(String companyName) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Company company = null;
transaction = session.beginTransaction();
company = new Company(companyName);
session.save(company);
transaction.commit();
session.close();
return company;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, то в результате работы программы мы получим, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7537 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.association.manytoone.DeveloperRunner Feb 22, 2016 11:54:37 AM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 22, 2016 11:54:37 AM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 22, 2016 11:54:37 AM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 22, 2016 11:54:37 AM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 22, 2016 11:54:38 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 22, 2016 11:54:38 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 22, 2016 11:54:38 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 22, 2016 11:54:38 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 22, 2016 11:54:38 AM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Mon Feb 22 11:54:38 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 22, 2016 11:54:39 AM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding company record... Creating developer's records... List of Developers: Feb 22, 2016 11:54:40 AM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Company: Company: Company ID: 1 Company Name: Proselyte.net ================ Developer: id: 2 First Name: Peter Last Name: Programmer Specialty: C++ Developer Experience: 2 Company: Company: Company ID: 1 Company Name: Proselyte.net ================ Updating experience of Proselyte to 3 years and removing Peter... Final list of Developers: Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Company: Company: Company ID: 1 Company Name: Proselyte.net ================ Feb 22, 2016 11:54:40 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
16. Виды связей. One-to-One.
В том случае, если поле одного из объектов содержит один единственный экземпляр другого объекта имеет смысл применить тип ассоциации One-to-One.
Для понимания того, как это работает на практике рассмотрим пример простого приложения.
16.1. Шаг 1. Создадим две таблицы в нашей базе данных (далее – БД)
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
CONTACT INT DEFAULT NULL,
PRIMARY KEY(ID)
);
HIBERNATE_CONTACTS
CREATE TABLE HIBERNATE_CONTACTS (
ID INT NOT NULL AUTO_INCREMENT,
ADDRESS VARCHAR(100) default NULL,
CITY VARCHAR(50) default NULL,
PHONE_NUMBER VARCHAR(20) default NULL,
EMAIL VARCHAR(100) default NULL,
PRIMARY KEY (id)
);
16.2. Шаг 2. Создаём POJO-классы
Developer.java
package net.proselyte.hibernate.association.onetoone;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private Contact contact;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience, Contact contact) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
this.contact = contact;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n" +
"Contact Information:\n" + contact;
}
}
Contact.java
package net.proselyte.hibernate.association.onetoone;
public class Contact {
private int id;
private String address;
private String city;
private String phoneNumber;
private String email;
public Contact() {
}
public Contact(String address, String city, String phoneNumber, String email) {
this.address = address;
this.city = city;
this.phoneNumber = phoneNumber;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Address: " + address +
"\nCity: " + city +
"\nPhone Number: " + phoneNumber +
"\nemail: " + email + "\n";
}
}
16.3. Шаг 3. Создаём конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost/ИМЯ ВАШЕЙ БАЗЫ ДАННЫХ
</property>
<property name="hibernate.connection.username">
ВАШЕ ИМЯ ПОЛЬЗОВАТЕЛЯ
</property>
<property name="hibernate.connection.password">
ВАШ ПАРОЛЬ
</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.association.onetoone.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer's detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
<many-to-one name="contact" column="CONTACT" not-null="true" unique="true"
class="net.proselyte.hibernate.association.onetoone.Contact" />
</class>
<class name="net.proselyte.hibernate.association.onetoone.Contact" table="HIBERNATE_CONTACTS">
<meta attribute="class-description">
This class contains the address detail.
</meta>
<id name="id" type="int" column="ID">
<generator class="native"/>
</id>
<property name="address" column="ADDRESS" type="string"/>
<property name="city" column="CITY" type="string"/>
<property name="phoneNumber" column="PHONE_NUMBER" type="string"/>
<property name="email" column="EMAIL" type="string"/>
</class>
</hibernate-mapping>
16.4. Шаг 4. Создаём класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.association.onetoone;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.List;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Adding contact information...");
Contact contact1 = developerRunner.addContact("Some address", "Dnipropetrovsk", "+380501234567", "proselytear@yahoo.com");
Contact contact2 = developerRunner.addContact("One More address", "Kyiv", "+380667654321", "peter@mail.com");
System.out.println("Creating developer's records...");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, contact1);
Integer developerId2 = developerRunner.addDeveloper("Peter", "Programmer", "C++ Developer", 2, contact2);
System.out.println("List of Developers: ");
developerRunner.listDevelopers();
System.out.println("Updating experience of Proselyte to 3 years and removing Peter...");
developerRunner.updateDeveloper(developerId1, 3);
developerRunner.removeDeveloper(developerId2);
System.out.println("Final list of Developers: ");
developerRunner.listDevelopers();
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, Contact contact) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience, contact);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public Contact addContact(String address, String city, String phoneNumber, String email) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Contact contact = new Contact(address, city, phoneNumber, email);
session.save(contact);
transaction.commit();
session.close();
return contact;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано верно, то в результате работы программы мы получим, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7540 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.association.onetoone.DeveloperRunner Feb 21, 2016 7:02:14 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 21, 2016 7:02:14 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 21, 2016 7:02:14 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 21, 2016 7:02:15 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 21, 2016 7:02:16 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Sun Feb 21 19:02:16 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 21, 2016 7:02:16 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding contact information... Creating developer's records... List of Developers: Feb 21, 2016 7:02:17 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 11 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Contact Information: Address: Some address City: Dnipropetrovsk Phone Number: +380501234567 email: proselytear@yahoo.com ================ Developer: id: 12 First Name: Peter Last Name: Programmer Specialty: C++ Developer Experience: 2 Contact Information: Address: One More address City: Kyiv Phone Number: +380667654321 email: peter@mail.com ================ Updating experience of Proselyte to 3 years and removing Peter... Final list of Developers: Developer: id: 11 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Contact Information: Address: Some address City: Dnipropetrovsk Phone Number: +380501234567 email: proselytear@yahoo.com ================ Feb 21, 2016 7:02:17 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
17. Виды связей. One-to-Many.
Связь One-to-Many (один ко многим) используется в том случае, когда один объект, в качестве одного из аттрибутов, имеет коллекцию других объектов.
Например, разработчик учавствовал в более, чем одном проекте, поэтому лучше использовать связь One-to-Many.
Для понимания того, как это работает на практике, рассмотрим пример простого приложения.
17.1. Шаг 1. Создадим две таблицы в базе данных (далее – БД)
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
mysql
create table HIBERNATE_PROJECTS (
id INT NOT NULL AUTO_INCREMENT,
PROJECT_NAME VARCHAR(50) default NULL,
COMPANY VARCHAR(50) default NULL,
DEVELOPER_ID INT default NULL,
PRIMARY KEY (id)
);
17.2. Шаг 2. Создадим POJO – классы
Developer.java
package net.proselyte.hibernate.association.onetomany;
import java.util.Set;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private Set projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public Set getProjects() {
return projects;
}
public void setProjects(Set projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
Project.java
package net.proselyte.hibernate.association.onetomany;
public class Project {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
/**
* Methods equals and hashCode for comparing objects
*/
public boolean equals(Object object) {
if (object == null) {
return false;
}
if (!this.getClass().equals(object.getClass())) {
return false;
}
Project object2 = (Project) object;
if ((this.id == object2.getId())
&& (this.projectName == object2.getProjectName())
&& (this.companyName == object2.getCompanyName())) {
return true;
}
return false;
}
public int hasCode() {
int code = 0;
code = (id + projectName + companyName).hashCode();
return code;
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
17.3. Шаг 3. Создадим конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ ВАШЕЙ БАЗЫ ДАННЫХ</property>
<property name="hibernate.connection.username">ВАШЕ ИЯ ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.association.onetomany.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains developer's details.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<set name="projects" cascade="all">
<key column="DEVELOPER_ID"/>
<one-to-many class="net.proselyte.hibernate.association.onetomany.Project"/>
</set>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.association.onetomany.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">This class contains project's records.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
17.4. Шаг 4. Создадим класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.association.onetomany;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the set of projects.");
HashSet projects1 = new HashSet();
projects1.add(new Project("Proselyte Tutorial", "proselyte.net"));
projects1.add(new Project("SkybleLib", "SkybleSoft"));
HashSet projects2 = new HashSet();
projects2.add(new Project("Some Project", "Some Company"));
projects2.add(new Project("One more Project", "One more Company"));
System.out.println("Adding developer's records to the DB");
int developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects1);
int developerId2 = developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects2);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(developerId1, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public int addDeveloper(String firstName, String lastName, String specialty, int experience, Set projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
int developerId = (int)session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
Set projects = developer.getProjects();
for (Project project : projects) {
System.out.println(project);
}
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано верно, то в результате работы программы мы получим примерно следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7533 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.association.onetomany.DeveloperRunner Feb 21, 2016 4:47:08 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 21, 2016 4:47:08 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 21, 2016 4:47:08 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 21, 2016 4:47:08 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 21, 2016 4:47:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 21, 2016 4:47:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 21, 2016 4:47:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 21, 2016 4:47:10 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 21, 2016 4:47:10 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Sun Feb 21 16:47:10 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 21, 2016 4:47:10 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Creating the set of projects. Adding developer's records to the DB List of developers Feb 21, 2016 4:47:11 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 5 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Project: id: 10 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 9 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 6 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 12 Project Name: Some Project Company Name: Some Company Project: id: 11 Project Name: One more Project Company Name: One more Company ================ =================================== Updating Proselyte Final list of developers Developer: id: 5 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Project: id: 9 Project Name: SkybleLib Company Name: SkybleSoft Project: id: 10 Project Name: Proselyte Tutorial Company Name: proselyte.net ================ Developer: id: 6 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Feb 21, 2016 4:47:11 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Project: id: 12 Project Name: Some Project Company Name: Some Company Project: id: 11 Project Name: One more Project Company Name: One more Company ================ ===================================
18. Виды связей. Many-to-Many.
Представим ситуацию, когда у нас есть несколько проектов, в каждом из которых участвуют несколько разработчиков. Для такого варианта подходит ассоциация Many-to-Many.
Для понимания того, как это работает на практике рассмотрим пример простого приложения.
18.1. Шаг 1. Создадим три таблицы в нашей базе данных (далее – БД).
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
HIBERNATE_PROJECTS
CREATE TABLE HIBERNATE_PROJECTS (
id INT NOT NULL AUTO_INCREMENT,
PROJECT_NAME VARCHAR(50) default NULL,
COMPANY VARCHAR(50) default NULL,
PRIMARY KEY (id)
);
HIBERNATE_DEV_PROJECTS
(промежуточная таблица, которая содержит идентификаторы разработчика и проекта)CREATE TABLE HIBERNATE_DEV_PROJECTS (
DEVELOPER_ID INT NOT NULL,
PROJECT_ID INT NOT NULL,
PRIMARY KEY (DEVELOPER_ID, PROJECT_ID)
);
18.2. Шаг 2. Создадим POJO – классы
Developer.java
package net.proselyte.hibernate.association.manytomany;
import java.util.Set;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private Set projects;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public Set getProjects() {
return projects;
}
public void setProjects(Set projects) {
this.projects = projects;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
Project.java
package net.proselyte.hibernate.association.manytomany;
public class Project {
private int id;
private String projectName;
private String companyName;
/**
* Constructors
*/
public Project() {
}
public Project(String projectName, String companyName) {
this.projectName = projectName;
this.companyName = companyName;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
/**
* Methods equals and hashCode for comparing objects
*/
public boolean equals(Object object) {
if (object == null) {
return false;
}
if (!this.getClass().equals(object.getClass())) {
return false;
}
Project object2 = (Project) object;
if ((this.id == object2.getId()) && (this.projectName == object2.getProjectName()) && (this.companyName == object2.getCompanyName())) {
return true;
}
return false;
}
public int hasCode() {
int code = 0;
code = (id + projectName + companyName).hashCode();
return code;
}
@Override
public String toString() {
return "Project:\n" +
"id: " + id +
"\nProject Name: " + projectName +
"\nCompany Name: " + companyName + "\n";
}
}
18.3. Шаг 3. Создаём конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver </property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БАЗЫ_ДАННЫХ</property>
<property name="hibernate.connection.username">ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ_ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.association.manytomany.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains developer details.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<set name="projects" cascade="save-update" table="HIBERNATE_DEV_PROJECTS">
<key column="DEVELOPER_ID" />
<many-to-many column="PROJECT_ID" class="net.proselyte.hibernate.association.manytomany.Project"/>
</set>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
<class name="net.proselyte.hibernate.association.manytomany.Project" table="HIBERNATE_PROJECTS">
<meta attribute="class-description">This class contains project details.</meta>
<id name="id" type="int" column="ID">
<generator class="native"/>
</id>
<property name="projectName" column="PROJECT_NAME" type="string"/>
<property name="companyName" column="COMPANY" type="string"/>
</class>
</hibernate-mapping>
18.4. Шаг 4. Создаём класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.association.manytomany;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Creating the set of projects.");
HashSet projects = new HashSet();
projects.add(new Project("Proselyte Tutorial", "proselyte.net"));
projects.add(new Project("SkybleLib", "SkybleSoft"));
System.out.println("Adding developer's records to the DB");
int developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, projects);
int developerId2 = developerRunner.addDeveloper("Peter", "UI", "UI Developer", 4, projects);
System.out.println("List of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
System.out.println("Updating Proselyte");
developerRunner.updateDeveloper(developerId1, 3);
System.out.println("Final list of developers");
developerRunner.listDevelopers();
System.out.println("===================================");
sessionFactory.close();
}
public int addDeveloper(String firstName, String lastName, String specialty, int experience, Set projects) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developer.setProjects(projects);
int developerId = (int)session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
Set projects = developer.getProjects();
for (Project project : projects) {
System.out.println(project);
}
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, то в результате работы программы мы получим, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7533 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.association.manytomany.DeveloperRunner Feb 22, 2016 12:29:48 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 22, 2016 12:29:48 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 22, 2016 12:29:48 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 22, 2016 12:29:48 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 22, 2016 12:29:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 22, 2016 12:29:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 22, 2016 12:29:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 22, 2016 12:29:50 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 22, 2016 12:29:50 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Mon Feb 22 12:29:50 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 22, 2016 12:29:50 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Creating the set of projects. Adding developer's records to the DB List of developers Feb 22, 2016 12:29:51 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 ================ Developer: id: 3 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Project: id: 2 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 1 Project Name: SkybleLib Company Name: SkybleSoft ================ Developer: id: 4 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 2 Project Name: Proselyte Tutorial Company Name: proselyte.net Project: id: 1 Project Name: SkybleLib Company Name: SkybleSoft ================ =================================== Updating Proselyte Final list of developers Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 ================ Developer: id: 3 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Feb 22, 2016 12:29:51 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop Project: INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] id: 1 Project Name: SkybleLib Company Name: SkybleSoft Project: id: 2 Project Name: Proselyte Tutorial Company Name: proselyte.net ================ Developer: id: 4 First Name: Peter Last Name: UI Specialty: UI Developer Experience: 4 Project: id: 1 Project Name: SkybleLib Company Name: SkybleSoft Project: id: 2 Project Name: Proselyte Tutorial Company Name: proselyte.net ================ ===================================
19. Виды связей. Component Mapping.
Часто мы встречаемся с ситуацией, когда один из нащих классов содержит в качестве своего поля ссылку на другой объект. Ранее мы уже рассматривали такую ситуацию, но применяли другие способы связывания (mapping).
В этой статье мы рассмотрим элемент под названием <component>.
Давайте рассмотрим пример простого приложения.
Пример:
Исходный код проекта можно скачать по ЭТОЙ ССЫЛКЕ.
-
Создадим таблицу в нашей базе данных (далее – БД).
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS ( id INT NOT NULL auto_increment, FIRST_NAME VARCHAR(20) default NULL, LAST_NAME VARCHAR(20) default NULL, SPECIALTY VARCHAR(40) default NULL , EXPERIENCE INT default NULL, COMPANY_NAME VARCHAR(40) default NULL, PRIMARY KEY (id) );
-
Создаём POJO – классы
Developer.java
package net.proselyte.hibernate.component; public class Developer { private int id; private String firstName; private String lastName; private String specialty; private int experience; private Company company; /** * Default Constructor */ public Developer() { } /** * Plain constructor */ public Developer(String firstName, String lastName, String specialty, int experience, Company company) { this.firstName = firstName; this.lastName = lastName; this.specialty = specialty; this.experience = experience; this.company = company; } /** * Getters and Setters */ public int getId() { return id; } public void setId(int id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getSpecialty() { return specialty; } public void setSpecialty(String specialty) { this.specialty = specialty; } public int getExperience() { return experience; } public void setExperience(int experience) { this.experience = experience; } public Company getCompany() { return company; } public void setCompany(Company company) { this.company = company; } /** * toString method (optional) */ @Override public String toString() { return "Developer:\n" + "id: " + id + "\nFirst Name: " + firstName + "\n" + "Last Name: " + lastName + "\n" + "Specialty: " + specialty + "\n" + "Experience: " + experience + "\n" + "Company: " + company; } }
Company.java
package net.proselyte.hibernate.component; public class Company { private int id; private String companyName; public Company() { } public Company(String companyName) { this.companyName = companyName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } @Override public String toString() { return "Company:" + "\nCompany ID: " + id + "\nCompany Name: " + companyName + "\n"; } }
-
Создадим конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <!-- Assume PROSELYTE_TUTORIAL is the database name --> <property name="hibernate.connection.url"> jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БАЗЫ_ДАННЫХ </property> <property name="hibernate.connection.username"> ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ </property> <property name="hibernate.connection.password"> ВАШ_ПАРОЛЬ </property> <!-- List of XML mapping files --> <mapping resource="Developer.hbm.xml"/> </session-factory> </hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="net.proselyte.hibernate.component.Developer" table="HIBERNATE_DEVELOPERS"> <meta attribute="class-description"> This class contains developer details. </meta> <id name="id" type="int" column="id"> <generator class="native"/> </id> <component name="company" class="net.proselyte.hibernate.component.Company"> <property name="companyName" column="COMPANY_NAME"/> </component> <property name="firstName" column="FIRST_NAME" type="string"/> <property name="lastName" column="LAST_NAME" type="string"/> <property name="specialty" column="SPECIALTY" type="string"/> <property name="experience" column="EXPERIENCE" type="int"/> </class> <class name="net.proselyte.hibernate.component.Company" table="HIBERNATE_COMPANIES"> <meta attribute="class-description"> This class contains company details. </meta> <id name="id" type="int" column="ID"> <generator class="native"/> </id> <property name="companyName" column="COMPANY_NAME" type="string"/> </class> </hibernate-mapping>
-
Создаём класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.component; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import java.util.List; public class DeveloperRunner { private static SessionFactory sessionFactory; public static void main(String[] args) { sessionFactory = new Configuration().configure().buildSessionFactory(); DeveloperRunner developerRunner = new DeveloperRunner(); System.out.println("Adding company1 record..."); Company company1 = developerRunner.addCompany("Proselyte.net"); Company company2 = developerRunner.addCompany("Some Company"); System.out.println("Creating developer's records..."); Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2, company1); Integer developerId2 = developerRunner.addDeveloper("Peter", "Programmer", "C++ Developer", 2, company2); System.out.println("List of Developers: "); developerRunner.listDevelopers(); System.out.println("Updating experience of Proselyte to 3 years and removing Peter..."); developerRunner.updateDeveloper(developerId1, 3); developerRunner.removeDeveloper(developerId2); System.out.println("Final list of Developers: "); developerRunner.listDevelopers(); sessionFactory.close(); } public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, Company company) { Session session = sessionFactory.openSession(); Transaction transaction = null; Integer developerId = null; transaction = session.beginTransaction(); Developer developer = new Developer(firstName, lastName, specialty, experience, company); developerId = (Integer) session.save(developer); transaction.commit(); session.close(); return developerId; } public Company addCompany(String companyName) { Session session = sessionFactory.openSession(); Transaction transaction = null; Company company = null; transaction = session.beginTransaction(); company = new Company(companyName); session.save(company); transaction.commit(); session.close(); return company; } public void listDevelopers() { Session session = sessionFactory.openSession(); Transaction transaction = null; transaction = session.beginTransaction(); List developers = session.createQuery("FROM Developer").list(); for (Developer developer : developers) { System.out.println(developer); System.out.println("\n================\n"); } session.close(); } public void updateDeveloper(int developerId, int experience) { Session session = sessionFactory.openSession(); Transaction transaction = null; transaction = session.beginTransaction(); Developer developer = (Developer) session.get(Developer.class, developerId); developer.setExperience(experience); session.update(developer); transaction.commit(); session.close(); } public void removeDeveloper(int developerId) { Session session = sessionFactory.openSession(); Transaction transaction = null; transaction = session.beginTransaction(); Developer developer = (Developer) session.get(Developer.class, developerId); session.delete(developer); transaction.commit(); session.close(); } }
Если всё было сделано правильно, то в результате работы программы мы получим, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7533 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.component.DeveloperRunner Feb 22, 2016 8:00:43 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 22, 2016 8:00:43 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 22, 2016 8:00:43 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 22, 2016 8:00:43 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 22, 2016 8:00:45 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 22, 2016 8:00:45 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 22, 2016 8:00:45 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 22, 2016 8:00:45 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 22, 2016 8:00:45 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Mon Feb 22 20:00:45 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 22, 2016 8:00:45 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding company1 record... Creating developer's records... List of Developers: Feb 22, 2016 8:00:46 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 Company: Company: Company ID: 0 Company Name: Proselyte.net ================ Developer: id: 2 First Name: Peter Last Name: Programmer Specialty: C++ Developer Experience: 2 Company: Company: Company ID: 0 Company Name: Some Company ================ Updating experience of Proselyte to 3 years and removing Peter... Final list of Developers: Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Company: Company: Company ID: 0 Company Name: Proselyte.net ================ Feb 22, 2016 8:00:47 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
В этой статье мы рассмотрели пример использования Component Mapping с помощью элемента <component>.
20. Аннотации.
Для mapping можно использовать конфигурационные XML – файлы для конфигурирования Hibernate. В этих XML – файлах указываются для Hibernate с какой таблицей в нашей базе данных (далее – БД) необходимо связать тот или иной POJO – класс и к каким колонкам относятся те или иные поля в этом классе. Но в Hibernate предусмотрена возможность конфигурирования приложения с помощью аннотаций.
Аннотации являются мощным инструментом для предоставления метаданных, а также намного нагляднее при чтении нашего кода другим разработчиком.
Обязательными аннотациями являются следующие:
-
@Entity
Эта аннотация указывает Hibernate, что данный класс является сущностью (entity bean). Такой класс должен иметь конструктор по-умолчанию (пустой конструктор). -
@Table
С помощью этой аннотации мы говорим Hibernate, с какой именно таблицей необходимо связать (map) данный класс. Аннотация@Table
имеет различные аттрибуты, с помощью которых мы можем указать имя таблицы, каталог, БД и уникальность столбцов в таблице БД. -
@Id
С помощью аннотации@Id
мы указываем первичный ключ (Primary Key) данного класса. -
@GeneratedValue
Эта аннотация используется вместе с аннотацией@Id
и определяет такие параметры, какstrategy
иgenerator
. -
@Column
Аннотация@Column
определяет к какому столбцу в таблице БД относится конкретное поле класса (аттрибут класса).
Наиболее часто используемые аттрибуты аннотации @Column
такие:
-
name
- Указывает имя столбца в таблице -
unique
Определяет, должно ли быть данное значение уникальным -
nullable
Определяет, может ли данное поле бытьNULL
, или нет. -
length
Указывает, какой размер столбца (например количество символов, при использованииString
).
Для понимания того, как это работает на практике, рассмотрим пример небольшого приложения.
20.1. Шаг 1. Создадим таблицу в нашей БД.
HIBERNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
FIRST_NAME VARCHAR(50) DEFAULT NULL,
LAST_NAME VARCHAR(50) DEFAULT NULL,
SPECIALTY VARCHAR(50) DEFAULT NULL,
EXPERIENCE INT DEFAULT NULL,
PRIMARY KEY(ID)
);
20.2. Шаг 2. Создадим POJO – класс.
Developer.java
package net.proselyte.hibernate.annotations;
import javax.persistence.*;
@Entity
@Table(name = "HIBERNATE_DEVELOPERS")
public class Developer {
@Id
@GeneratedValue (strategy = GenerationType.AUTO)
@Column (name = "id")
private int id;
@Column (name = "FIRST_NAME")
private String firstName;
@Column (name = "LAST_NAME")
private String lastName;
@Column (name = "SPECIALTY")
private String specialty;
@Column (name = "EXPERIENCE")
private int experience;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nFirst Name: " + firstName + "\n" +
"Last Name: " + lastName + "\n" +
"Specialty: " + specialty + "\n" +
"Experience: " + experience + "\n";
}
}
20.3. Шаг 3. Создадим конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БД</property>
<property name="hibernate.connection.username">ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ_ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.annotations.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains developer details.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
</class>
</hibernate-mapping>
20.4. Шаг 4. Создадим класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.annotations;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import java.util.List;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Adding Developer's records to the database");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 2);
Integer developerId2 = developerRunner.addDeveloper("Some", "Developer", "C++ Developer", 2);
Integer developerId3 = developerRunner.addDeveloper("Peter", "Team Lead", "Java Team Lead", 6);
System.out.println("List of Developers:");
developerRunner.listDevelopers();
System.out.println("Removing \'Some Developer\' and updating \'Proselyte Developer\''s experience:");
developerRunner.removeDeveloper(developerId2);
developerRunner.updateDeveloper(developerId1, 3);
System.out.println("Final list of Developers:");
developerRunner.listDevelopers();
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
List developers = session.createQuery("FROM Developer").list();
for (Developer developer : developers) {
System.out.println(developer);
System.out.println("\n================\n");
}
session.close();
}
public void updateDeveloper(int developerId, int experience) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
developer.setExperience(experience);
session.update(developer);
transaction.commit();
session.close();
}
public void removeDeveloper(int developerId) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Developer developer = (Developer) session.get(Developer.class, developerId);
session.delete(developer);
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, то в результате работы программы вы получите, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7536 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.annotations.DeveloperRunner Feb 22, 2016 9:34:01 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 22, 2016 9:34:01 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 22, 2016 9:34:01 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 22, 2016 9:34:01 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 22, 2016 9:34:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 22, 2016 9:34:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 22, 2016 9:34:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 22, 2016 9:34:02 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 22, 2016 9:34:02 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Mon Feb 22 21:34:03 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 22, 2016 9:34:03 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding Developer's records to the database List of Developers: Feb 22, 2016 9:34:04 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService INFO: HHH000397: Using ASTQueryTranslatorFactory Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 2 ================ Developer: id: 2 First Name: Some Last Name: Developer Specialty: C++ Developer Experience: 2 ================ Developer: id: 3 First Name: Peter Last Name: Team Lead Specialty: Java Team Lead Experience: 6 ================ Removing 'Some Developer' and updating 'Proselyte Developer''s experience: Final list of Developers: Developer: id: 1 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 ================ Developer: id: 3 First Name: Peter Last Name: Team Lead Specialty: Java Team Lead Experience: 6 ================
Так будет выглядеть наша таблица HIBERNATE_DEVELOPERS
в БД
ID | FIRST_NAME | LAST_NAME | SPECIALTY | EXPERIENCE | |
---|---|---|---|---|---|
1 |
Proselyte |
Developer |
Java Developer |
3 |
|
3 |
Peter |
Team Lead |
Java Team Lead |
6 |
21. Язык запросов Hibernate (HQL).
HQL (Hibernate Query Language) – это объектно-ориентированный (далее – ОО) язык запросов, который крайне похож на SQL.
Отличие между HQL и SQL состоит в том, что SQL работает таблицами в базе данных (далее – БД) и их столбцами, а HQL – с сохраняемыми объектами (Persistent Objects) и их полями (аттрибутами класса).
Hibernate транслирует HQL – запросы в понятные для БД SQL – запросы, которые и выполняют необходимые нам действия в БД.
Мы также имеем возможность использовать обычные SQL – запросы в Hibernate используя Native SQL, но использование HQL является более предпочтительным.
Давайте рассмотрим основные ключевые слова языка HQL.
21.1. FROM
Если мы хотим загрузить в память наши сохраняемые объекты, то мы будем использовать ключевое слово FROM
. Вот пример его использования:
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery("FROM Developer");
List developers = query.list();
}
}
Developer
– это POJO – класс Developer.java
, который ассоциирован с таблицей в БД.
21.2. INSERT
Мы используем ключевое слово INSERT
, в том случае, если хотим добавить запись в таблицу нашей БД.
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery("INSERT INTO Developer (firstName, lastName, specialty, experience)");
}
}
21.3. UPDATE
Ключевое слово UPDATE
используется для обновления одного или нескольких полей объекта.
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery("UPDATE Developer SET experience =: experience WHERE id =: developerId");
query.setParameter("experience", 3);
}
}
21.4. DELETE
Это ключевое слово используется для удаления одного или нескольких объектов.
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery("DELETE FROM Developer WHERE id = :developerId");
query.setParameter("developerId", 1);
}
}
21.5. SELECT
Если мы хотим получить запись из таблицы нашей БД, то мы должны использовать ключевое слово SELECT
.
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery("SELECT D.lastName FROM Developer D");
List developers = query.list();
}
}
21.6. AS
В предыдущем примере использовалась запись формы Developer D
. С использованием опционального ключевого слова AS
это будет выглядеть так:
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery("FROM Developer AS D");
List developers = query.list();
}
}
21.7. WHERE
В том случае, если мы хотим получить объекты, которые соответствуют определённым параметрам, то мы должны использовать ключевое слово WHERE
.
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery("FROM Developer D WHERE D.id = 1");
List developer = query.list();
}
}
21.8. ORDER BY
Для того чтобы отсортировать список объектов, полученных в результате запроса, мы должны применить ключевое слово ORDER BY
. Нам необходимо указать параметр, по которому список будет отсортирован и тип сортировки – по возрастанию (ASC
) или по убыванию (DESC
).
public class Program {
// ...
public void executeHqlQuery() {
Query query =
session.createQuery("FROM Developer D WHERE experience > 3 ORDER BY D.experience DESC");
}
}
21.9. GROUP BY
С помощью ключевого слова GROUP BY
мы можем группировать данные, полученные из БД по какому-либо признаку.
public class Program {
// ...
public void executeHqlQuery() {
Query query = session.createQuery(
"SELECT MAX(D.experience), D.lastName, D.specialty FROM Developer D GROUP BY D.lastName");
List developers = query.list();
}
}
21.10. Методы агрегации
Язык запросов Hibernate (HQL) поддерживает различные методы агрегации, которые доступны и в SQL. HQL поддерживает следующие методы:
-
min(имя свойства)
— минимальное значение данного свойства. -
max(имя свойства)
— максимальное значение данного свойства. -
sum(имя свойства)
— сумма всех значений данного свойства. -
avg(имя свойства)
— среднее арифметическое всех значений данного свойства -
count(имя свойства)
— какое количество раз данное свойство встречается в результате.
22. Запросы с использованием Criteria
Hibernate
поддерживает различные способы манипулирования объектами и транслирования их в таблицы баз данных (далее – БД).
Одним из таких способов является Criteria API
, который позволяет создавать запросы с критериями, программным методом.
Для создания Criteria
используется метод createCriteria()
интерфейса Session
.
Этот метод возвращает экземпляр сохраняемого класса (persistent class
) в результате его выполнения.
Вот как это выглядит на практике:
public class Program {
// ...
public static void main(String[] args){
Criteria criteria = session.createCriteria(Developer.class);
List developers = criteria.list();
}
}
Criteria
имеет два важных метода:
-
public Criteria setFirstResult(int firstResult)
Этот метод указывает первый ряд результата, который начинается с 0.
-
public Criteria setMaxResults(int maxResults)
Этот метод ограничивает максимальное количество объектов, которое Hibernate
сможет получить в результате запроса.
Для понимания того, как это работает на практике рассмотрим пример простого приложения.
22.1. Шаг 1. Создание таблицы HIBERNATE_DEVELOPERS
в БД.
CREATE TABLE HIBERNATE_DEVELOPERS (
id INT NOT NULL auto_increment,
FIRST_NAME VARCHAR(50) default NULL,
LAST_NAME VARCHAR(50) default NULL,
SPECIALTY VARCHAR(50) default NULL,
EXPERIENCE INT default NULL,
SALARY INT default NULL,
PRIMARY KEY (id)
);
22.2. Шаг 2. Создание POJO–класса
Developer.java
package net.proselyte.hibernate.criteria;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private int salary;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience, int salary) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
this.salary = salary;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "id: " + id +
"\nFirst Name: " + firstName +
"\nLast Name: " + lastName +
"\nSpecialty: " + specialty +
"\nExperience: " + experience +
"\nSalary: " + salary + "\n";
}
}
22.3. Шаг 3. Создание конфигурационных файлов
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БД</property>
<property name="hibernate.connection.username">ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ_ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.criteria.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains developer details.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
<property name="salary" column="SALARY" type="int"/>
</class>
</hibernate-mapping>
22.4. Шаг 4. Создание класса DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.criteria;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import java.util.List;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Adding developer's records to the database...");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 3, 2000);
Integer developerId2 = developerRunner.addDeveloper("First", "Developer", "C++ Developer", 10, 2000);
Integer developerId3 = developerRunner.addDeveloper("Second", "Developer", "C# Developer", 5, 2000);
Integer developerId4 = developerRunner.addDeveloper("Third", "Developer", "PHP Developer", 1, 2000);
System.out.println("List of Developers with experience more than 3 years:");
developerRunner.listDevelopersOverThreeYears();
System.out.println("Total Salary of all Developers:");
developerRunner.totalSalary();
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, int salary) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience, salary);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopersOverThreeYears() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Developer.class);
criteria.add(Restrictions.gt("experience", 3));
List developers = criteria.list();
for (Developer developer : developers) {
System.out.println("=======================");
System.out.println(developer);
System.out.println("=======================");
}
transaction.commit();
session.close();
}
public void totalSalary() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Developer.class);
criteria.setProjection(Projections.sum("salary"));
List totalSalary = criteria.list();
System.out.println("Total salary of all developers: " + totalSalary.get(0));
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, то в результате работы программы получится, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7536 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.criteria.DeveloperRunner Feb 23, 2016 1:58:13 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 23, 2016 1:58:13 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 23, 2016 1:58:13 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 23, 2016 1:58:14 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 23, 2016 1:58:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 23, 2016 1:58:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 23, 2016 1:58:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 23, 2016 1:58:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 23, 2016 1:58:15 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Tue Feb 23 13:58:15 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 23, 2016 1:58:16 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding developer's records to the database... List of Developers with experience more than 3 years: ======================= id: 18 First Name: First Last Name: Developer Specialty: C++ Developer Experience: 10 Salary: 2000 ======================= ======================= id: 19 First Name: Second Last Name: Developer Specialty: C# Developer Experience: 5 Salary: 2000 ======================= Total Salary of all Developers: Total salary of all developers: 8000 Feb 23, 2016 1:58:16 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
23. Нативный SQL.
Для того чтобы формировать запросы для базы данных (далее – БД), при этом используя все возможности БД, может использоваться нативный SQL.
Таким образом приложение создаст нативный SQL – запрос, используя метод createSQLQuery()
интерфейса Session
, который выглядит следующим образом:
-
public SQLQuery createSQLQuery (String sqlString) throws HibernateException;
После того как методу createSQLQuery()
передать строку (String)
, содержащая SQL–запрос, можно связать результат этого запроса с сохраняемым объектом (persistent object)
.
Для понимания того, как это работает на практике, рассмотрим пример простого приложения.
Пример:
Исходный код проекта можно скачать по ЭТОЙ ССЫЛКЕ.
23.1. Шаг 1. Создание таблицы HIBRNATE_DEVELOPERS
в БД
CREATE TABLE HIBERNATE_DEVELOPERS (
id INT NOT NULL auto_increment,
FIRST_NAME VARCHAR(50) default NULL,
LAST_NAME VARCHAR(50) default NULL,
SPECIALTY VARCHAR(50) default NULL,
EXPERIENCE INT default NULL,
SALARY INT default NULL,
PRIMARY KEY (id)
);
23.2. Шаг 2. Создание POJO–класса
Developer.java
package net.proselyte.hibernate.criteria;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private int salary;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience, int salary) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
this.salary = salary;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "id: " + id +
"\nFirst Name: " + firstName +
"\nLast Name: " + lastName +
"\nSpecialty: " + specialty +
"\nExperience: " + experience +
"\nSalary: " + salary + "\n";
}
}
23.3. Шаг 3. Создание конфигурационных файлов
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БД
</property>
<property name="hibernate.connection.username">
ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ
</property>
<property name="hibernate.connection.password">
ВАШ_ПАРОЛЬ
</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.nativesql.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer details.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
<property name="salary" column="SALARY" type="int"/>
</class>
</hibernate-mapping>
23.4. Шаг 4. Создание класса DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.nativesql;
import org.hibernate.*;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Projections;
import java.util.List;
import java.util.Map;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Adding developer's records to the database...");
Integer developerId1 = developerRunner.addDeveloper("Proselyte", "Developer", "Java Developer", 3, 2000);
Integer developerId2 = developerRunner.addDeveloper("First", "Developer", "C++ Developer", 10, 5000);
Integer developerId3 = developerRunner.addDeveloper("Second", "Developer", "C# Developer", 5, 4000);
Integer developerId4 = developerRunner.addDeveloper("Third", "Developer", "PHP Developer", 1, 1000);
System.out.println("List of Developers using Entity Query:");
developerRunner.listDevelopers();
System.out.println("List of Developers using Scalar Query:");
developerRunner.listDevelopersScalar();
sessionFactory.close();
}
public Integer addDeveloper(String firstName, String lastName, String specialty, int experience, int salary) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
Developer developer = new Developer(firstName, lastName, specialty, experience, salary);
developerId = (Integer) session.save(developer);
transaction.commit();
session.close();
return developerId;
}
public void listDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
SQLQuery sqlQuery = session.createSQLQuery("SELECT * FROM HIBERNATE_DEVELOPERS");
sqlQuery.addEntity(Developer.class);
List developers = sqlQuery.list();
for (Developer developer : developers) {
System.out.println("=======================");
System.out.println(developer);
System.out.println("=======================");
}
transaction.commit();
session.close();
}
public void listDevelopersScalar() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
SQLQuery sqlQuery = session.createSQLQuery("SELECT * FROM HIBERNATE_DEVELOPERS");
sqlQuery.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List developers = sqlQuery.list();
for (Object developer : developers) {
Map row = (Map) developer;
System.out.println("=======================");
System.out.println("id: " + row.get("id"));
System.out.println("First Name: " + row.get("FIRST_NAME"));
System.out.println("Last Name: " + row.get("LAST_NAME"));
System.out.println("Specialty: " + row.get("SPECIALTY"));
System.out.println("Experience: " + row.get("EXPERIENCE"));
System.out.println("Salary: " + row.get("SALARY"));
System.out.println("=======================");
}
transaction.commit();
session.close();
}
public void totalSalary() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Developer.class);
criteria.setProjection(Projections.sum("salary"));
List totalSalary = criteria.list();
System.out.println("Total salary of all developers: " + totalSalary.get(0));
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, в результате работы программы получится, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7541 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.nativesql.DeveloperRunner Feb 23, 2016 9:44:03 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 23, 2016 9:44:03 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 23, 2016 9:44:03 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 23, 2016 9:44:04 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 23, 2016 9:44:05 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 23, 2016 9:44:05 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 23, 2016 9:44:05 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 23, 2016 9:44:05 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 23, 2016 9:44:05 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Tue Feb 23 21:44:05 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 23, 2016 9:44:05 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding developer's records to the database... List of Developers using Entity Query: ======================= id: 69 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Salary: 2000 ======================= ======================= id: 70 First Name: First Last Name: Developer Specialty: C++ Developer Experience: 10 Salary: 5000 ======================= ======================= id: 71 First Name: Second Last Name: Developer Specialty: C# Developer Experience: 5 Salary: 4000 ======================= ======================= id: 72 First Name: Third Last Name: Developer Specialty: PHP Developer Experience: 1 Salary: 1000 ======================= List of Developers using Scalar Query: ======================= id: 69 First Name: Proselyte Last Name: Developer Specialty: Java Developer Experience: 3 Salary: 2000 ======================= ======================= id: 70 First Name: First Last Name: Developer Specialty: C++ Developer Experience: 10 Salary: 5000 ======================= ======================= id: 71 First Name: Second Last Name: Developer Specialty: C# Developer Experience: 5 Salary: 4000 ======================= ======================= id: 72 First Name: Third Last Name: Developer Specialty: PHP Developer Experience: 1 Salary: 1000 ======================= Feb 23, 2016 9:44:06 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
id | FIRST_NAME | LAST_NAME | SPECIALTY | EXPERIENCE | SALARY | |
---|---|---|---|---|---|---|
65 |
Proselyte |
Developer |
Java Developer |
3 |
2000 |
|
66 |
First |
Developer |
C++ Developer |
10 |
5000 |
|
67 |
Second |
Developer |
C# Developer |
5 |
4000 |
|
68 |
Third |
Developer |
PHP Developer |
1 |
1000 |
24. Hibernate cache
Кэш - это память с большей скоростью доступа, предназначенная для ускорения обращения к данным, содержащимся постоянно в памяти с меньшей скоростью доступа. Кэширование применяется ЦПУ, жёсткими дисками, браузерами, веб-серверами, службами DNS и WINS.
Кэш состоит из набора записей. Каждая запись ассоциирована с элементом данных или блоком данных (небольшой части данных), которая является копией элемента данных в основной памяти. Каждая запись имеет идентификатор, часто называемый тегом, определяющий соответствие между элементами данных в кэше и их копиями в основной памяти.
Когда клиент кэша (ЦПУ, веб-браузер, операционная система) обращается к данным, прежде всего исследуется кэш. Если в кэше найдена запись с идентификатором, совпадающим с идентификатором затребованного элемента данных, то используются элементы данных в кэше. Такой случай называется попаданием кэша. Если в кэше не найдена запись, содержащая затребованный элемент данных, то он читается из основной памяти в кэш, и становится доступным для последующих обращений. Такой случай называется промахом кэша.
Кеширование является одним из способов оптимизации работы приложения, ключевой задачей которого является уменьшить количество прямых обращений к базе данных.
Кэширование, как правило, есть смысл применять в больших, высоконагруженных проектах, с десятками тысяч запросов в минуту. В таких проектах, чтобы не перегружать базу, как правило, кэшируют обращения к репозиторию. Особенно если известно, что данные из какой-нибудь мастер-системы обновляются с некоторой периодичностью. Если же проект маленький и перегрузки ему не грозят, тогда, конечно, лучше ничего не кэшировать — всегда свежие данные всегда лучше периодически обновляемых.
В Hibernate имеется 3 вида кеширования:
-
First-level cache (Кеш первого уровня);
-
Second-level cache (Кеш второго уровня);
-
Query cache (Кеш запросов);
24.1. Кеш первого уровня
В Hibernate кэш первого уровня представлен интерфейсом Session
, который является расширением к JPA EntityManager
. В терминологии JPA кэш первого уровня называется Persistence Context, и он представлен интерфейсом EntityManager
.
Кеш первого уровня всегда привязан к объекту Session
. Hibernate по умолчанию использует этот кеш и его нельзя отключить.
В JPA и Hibernate кэш первого уровня - это Java Map, в котором ключ представлен объектом, инкапсулирующим имя сущности и ее идентификатор, а значение - это сам объект сущности. Поэтому в JPA EntityManager
или Hibernate Session
может быть только одна сущность, хранящаяся с использованием одного и того же идентификатора и типа класса сущности. Причина, по которой невозможно иметь не более одного представления сущности, хранящегося в кэше первого уровня, заключается в том, что в противном случае мы можем получить различные отображения одной и той же строки базы данных, не зная, какая из них является правильной версией, синхронизируемой с соответствующей записью базы данных.
Session session = sessionFactory.openSession();
Pet pet = (Pet) session.load(Pet.class, id);
pet = (Pet) session.load(Pet.class, id);
В примере выше будет выполнен 1 запрос в базу, несмотря на то, что делается 2 вызова load()
, так как эти вызовы происходят в контексте одной сессии. Во время второй попытки загрузить план с тем же идентификатором будет использован кеш сессии.
Сначала Hibernate проверяет, хранится ли сущность в кэше первого уровня, и если да, то возвращается текущая управляемая ссылка на нее. Если сущность не найдена в кэше первого уровня, то Hibernate загрузит ее из базы данных с помощью SQL-запроса.
При использовании методов save()
, update()
, saveOrUpdate()
, load()
, get()
, list()
, iterate()
, scroll()
всегда будет задействован кеш первого уровня.
Для работы с данными кеша в сессии имеются следующие методы:
-
flush()
— обновляет объекты кеша с базой данных. -
evict()
— удаляет объект из кеша. -
contains()
— определяет, находится ли объект в кеше или нет. -
clear()
— очищает весь кеш.
При кеше первого уровня все активные сессии работает изолированно друг от друга, и у каждой сессии соответственно имеется свой кеш, который очищается при закрытии сессии.
24.2. Кеш второго уровня
Кеш второго уровня привязан к SessionFactory
, поэтому видимость этого кеша гораздо шире кеша первого уровня. Данный вид кеша доступен на протяжении всего времени работы приложения.
По умолчанию кеш второго уровня отключен. Для включения необходимо при конфигурировании настроек Hibernate в строке hibernate.cache.use_second_level_cache
установить значение true
.
На самом деле, Hibernate сам не реализует кеширование как таковое, а лишь предоставляет структуру для его реализации. Поэтому подключить можно любую реализацию, которая соответствует спецификации ORM фреймворка.
Из сказанного выше следует, что помимо hibernate.cache.use_second_level_cache
еще необходимо в настройках указать провайдера кеша hibernate.cache.region.factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory
, которого также нужно добавить в зависимости Maven.
spring:
jpa:
properties:
hibernate:
cache:
use_second_level_cache: true
region:
factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory
Из популярных реализаций провайдеров можно выделить следующие:
-
EHCache
-
OSCache
-
SwarmCache
-
JBoss TreeCache
Эти провайдеры дополнительно можно настраивать, например в файле ehcache.xml
, который должен находиться в директории ресурсов. В данном файле, например, можно ограничить количество записей в кеше maxElementsInMemory="500"
.
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="500"
eternal="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
overflowToDisk="true"/>
<cache name="cache1"
maxElementsInMemory="500"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>
Помимо вышеуказанного нужно еще самому Hibernate, что именно кешировать. Делается это с помощью аннотаций @Cacheable
и @Cache
.
@Entity
@Table(name = "shared_doc")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SharedDoc{
private Set<User> users;
}
@Cacheable
это аннотация JPA и позволяет объекту быть закэшированным. Hibernate поддерживает эту аннотацию в том же ключе. @Cache
это аннотация Hibernate, настраивающая тонкости кэширования объекта в кэше второго уровня Hibernate. Аннотации @Cacheable
достаточно, чтобы объект начал кэшироваться с настройками по умолчанию. При этом @Cache
, использованная без @Cacheable
, не разрешит кэширование объекта.
Аннотация @Cache
имеет несколько атрибутов:
-
usage
- отвечает за стратегию кеширования. -
region
- это логический разделитель памяти вашего кеша. Для каждого региона можно настроить свою политику кеширования (для EhCache в том жеehcache.xml
). Если регион не указан, то используется регион по умолчанию, который имеет полное имя вашего класса для которого применяется кеширование. -
include
- Могут ли свойства сущности, указанные сlazy=true
, кэшироваться, когда разрешена "ленивая" выборка на уровне атрибутов. По-умолчанию all и может быть также non-lazy
Стратегии кеширования определяют поведения кеша в определенных ситуациях. Выделяют четыре группы:
-
Read-only - используется только для сущностей, которые никогда не изменяются (выбрасывается исключение при попытке обновить такую сущность). Это очень просто и эффективно. Очень подходит для некоторых статических эталонных данных, которые не меняются.
-
Nonstrict-read-write - кэш обновляется после фиксации транзакции, которая изменила затронутые данные. Таким образом, строгая согласованность не гарантируется, и существует небольшое временное окно, в течение которого устаревшие данные могут быть получены из кэша. Этот тип стратегии подходит для вариантов использования, которые могут допустить возможную согласованность.
-
Read-write - эта стратегия гарантирует строгую согласованность, которая достигается за счет использования «мягких» блокировок - когда кэшированный объект обновляется, программная блокировка также сохраняется в кэше для этого объекта, которая освобождается после фиксации транзакции. Все параллельные транзакции, которые обращаются к заблокированным записям, будут извлекать соответствующие данные непосредственно из базы данных.
-
Transactional - изменения кэша выполняются в распределенных транзакциях. Изменение в кэшированном объекте либо фиксируется, либо откатывается как в базе данных, так и в кэше в одной и той же транзакции.
Помимо вышесказанного, следует помнить — зависимости Вашего класса по умолчанию также не кешируются. Например, если рассмотреть класс выше — SharedDoc
, то при выборке коллекция users
будет доставаться из базы данных, а не из кеша второго уровня. Если Вы хотите также кешировать и зависимости, то необходимо также проаннотировать все необходимы объекты аннотацией @Cache
.
@Entity
@Table(name = "shared_doc")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SharedDoc{
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<User> users;
}
Еще одна важная деталь про кеш второго уровня. Hibernate не хранит сами объекты классов. Он хранит информацию в разобранном (гидратированном) состоянии, в виде массивов строк, чисел и т. д.
-
Id
(первичный ключ) не сохраняется (он хранится как часть ключа кэша) -
Переходные свойства не сохраняются
-
Коллекции не хранятся
-
Значения свойств, не связанные с ассоциацией, хранятся в исходном виде
-
Для отношений ToOne сохраняется только идентификатор (внешний ключ)
Идентификатор объекта выступает указателем на эту информацию. Концептуально это нечто вроде Map, в которой id
объекта — ключ, а массивы данных — значение. Приблизительно можно представить себе это так - 1 → { "Pupkin", 1, null , {1,2,5} }
, что есть очень разумно, учитывая сколько лишней памяти занимает каждый объект.
Как уже говорилось выше, сначала Hibernate проверяет, хранится ли сущность в кэше первого уровня, затем проверяется кеш второго уровня, и только после этого, если запрашиваемый объект не найден, Hibernate выполнит запрос в базу данных.
Session session = factory.openSession();
Pet pet = (Pet) session.load(Pet.class, 1L);
session.close();
session = factory.openSession();
pet = (Pet) session.load(Pet.class, 1L);
session.close();
В примере выше, если будет реализован кеш второго уровня, то к базе данных будет выполнен всего один запрос.
24.3. Кеш запросов
Кэш запросов, так же как и кэш второго уровня, существует на уровне SessionFactory
и доступен во всех persistence context. Для кэша запросов требуются две дополнительные области физического кэша, в которых хранятся результаты кэшированного запроса и отметки времени последнего обновления таблицы.
Кэши первого и второго уровней работают с объектами загружаемыми по id
. Но к базе данных чаще выполняются запросы с условиями, чем загружаются какие-то заранее известные объекты. И результат выполнения таких запросов тоже может потребоваться кэшировать. Например, если вы делаете поисковый сайт по автозапчастям, то можете кэшировать запросы пользователей, которые, скорее всего, ищут одни запчасти гораздо чаще других.
У кэша запросов есть и своя цена — Hibernate будет вынужден отслеживать сущности закешированные с определённым запросом и выкидывать запрос из кэша, если кто-то поменяет значение сущности. То есть для кэша запросов стратегия параллельного доступа всегда read-only. По этим причинам, Hibernate по-умолчанию выключает кэширование запросов.
Кеш запросов похож на кеш второго уровня. Но в отличии от него — ключом к данным кеша выступает не идентификатор объекта, а совокупность параметров запроса. А сами данные — это идентификаторы объектов соответствующих критериям запроса. Таким образом, этот кеш рационально использовать с кешем второго уровня.
Если у вас есть запросы, выполняющиеся снова и снова, с одними и теми же параметрами, кэширование запросов предоставит выигрыш в производительности.
Для включения нужно добавить свойства hibernate.cache.use_query_cache=true
. Установив свойства значение true, вы заставляете Hibernate создавать необходимые кеши в памяти для хранения наборов запросов и идентификаторов. Также надо установить setCacheable(true)
.
spring:
jpa:
properties:
hibernate:
cache:
use_query_cache: true
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
query.setCacheRegion("employee");
List users = query.list();
SessionFactory.closeSession();
Hibernate также поддерживает очень тонкую поддержку кэша благодаря концепции области кэша. Регион кеша является частью кеша, которому дано имя. Пример выше использует метод, чтобы сообщить Hibernate хранить и искать запрос в области кэша сотрудников.
25. Кеширование
Кеширование является одним из способов оптимизации работы приложения, ключевой задачей которого является уменьшить количество прямых обращений к базе данных (далее – БД).
Если речь идёт о Hibernate, то схематически кэширование можно представить в виде следующего изображения:
25.1. First Level Cache (Кэш первого уровня)
Кэш первого уровня – это кэш Сессии (Session), который является обязательным. Через него проходят все запросы. Перед тем, как отправить объект в БД, сессия хранит объект за счёт своих ресурсов.
В том случае, если выполняем несколько обновлений объекта, Hibernate старается отсрочить (насколько это возможно) обновление для того, чтобы сократить количество выполненных запросов. Если закроем сессию, то все объекты, находящиеся в кэше теряются, а далее – либо сохраняются, либо обновляются.
25.2. Second level Cache (Кэш второго уровня)
Кэш второго уровня является необязательным (опциональным) и изначально Hibernate будет искать необходимый объект в кэше первого уровня. В основном, кэширование второго уровня отвечает за кэширование объектов.
25.3. Query Cache (Кэш запросов)
В Hibernate предусмотрен кэш для запросов и он интегрирован с кэшем второго уровня. Это требует двух дополнительных физических мест для хранения кэшированных запросов и временных меток для обновления таблицы БД. Этот вид кэширования эффективен только для часто используемых запросов с одинаковыми параметрами.
Рассмотрим, как это выглядит на практике.
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.nativesql.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">
This class contains developer details.
</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
<property name="salary" column="SALARY" type="int"/>
</class>
</hibernate-mapping>
hibenrate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БАЗЫ_ДАННЫХ</property>
<property name="hibernate.connection.username">ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ_ПАРОЛЬ</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Два предыдущих файла уже знакомы, а теперь нам необходимо создать файл ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="500"
eternal="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
overflowToDisk="true"
/>
<cache
name="Developer"
maxElementsInMemory="200"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/>
</ehcache>
Для того, что б кэширование стало доступным для нашего приложения мы должны активировать его следующим образом:
public class Program {
public static void main(String[] args){
Session session = sessionFactory.openSession();
Query query = session.createQuery("FROM HIBERNATE_DEVELOPERS");
query.setCacheable(true);
query.setCacheRegion("developer");
List developers = query.list();
sessionFactory.close();
}
}
26. Обработка “пакетов”.
Необходимо выполнить 100,000 записей в таблицу базы данных (далее – БД). Рассмотрим примитивный способ:
public class Program {
public static void main(String[] args) {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for(int i = 0; i < 100_000; i++){
Developer developer = new Developer(/*Some parameters*/);
session.save(developer);
}
transaction.commit();
session.close();
}
}
На первый взгляд кажется, что получим 100_000 записей в нашу БД, но на практике получим OutOfMemoryException
примерно в тот момент, когда попытаемся выполнить 50_000 – тысячную запись. Это вызвано тем, что Hibernate кэширует все сохраняемые объекты в кэш сессии.
Как же нам решить данную проблему?
Для этого нам необходимо использовать batch processing (обработку пакетов). Например, говорим Hibernate, что хотим вставлять каждые 50 объектов, как единый пакет. Для этого необходимо установить hibernate.jdbc.atch_size
на 50
и написать, примерно, такой кусок кода:
public class Program {
public static void main(String[] args) {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
for(int i = 0; i < 100000; i++) {
Developer developer = new Developer(/*Some parameters*/);
session.save(developer);
if (i % 50 == 0) {
session.flush();
session.clear();
}
}
transaction.commit();
session.close();
}
}
Для понимания того, как это работает на практике – рассмотрим пример небольшого приложения.
26.1. Шаг 1. Создаём таблицу в нашей БД
HIBRNATE_DEVELOPERS
CREATE TABLE HIBERNATE_DEVELOPERS (
id INT NOT NULL auto_increment,
FIRST_NAME VARCHAR(50) default NULL,
LAST_NAME VARCHAR(50) default NULL,
SPECIALTY VARCHAR(50) default NULL,
EXPERIENCE INT default NULL,
SALARY INT default NULL,
PRIMARY KEY (id)
);
26.2. Шаг 2. Создадим POJO
– класс
Developer.java
package net.proselyte.hibernate.batch;
public class Developer {
private int id;
private String firstName;
private String lastName;
private String specialty;
private int experience;
private int salary;
/**
* Default Constructor
*/
public Developer() {
}
/**
* Plain constructor
*/
public Developer(String firstName, String lastName, String specialty, int experience, int salary) {
this.firstName = firstName;
this.lastName = lastName;
this.specialty = specialty;
this.experience = experience;
this.salary = salary;
}
/**
* Getters and Setters
*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public int getExperience() {
return experience;
}
public void setExperience(int experience) {
this.experience = experience;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
/**
* toString method (optional)
*/
@Override
public String toString() {
return "id: " + id +
"\nFirst Name: " + firstName +
"\nLast Name: " + lastName +
"\nSpecialty: " + specialty +
"\nExperience: " + experience +
"\nSalary: " + salary + "\n";
}
}
26.3. Шаг 3. Создаём конфигурационные файлы
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Assume PROSELYTE_TUTORIAL is the database name -->
<property name="hibernate.connection.url">jdbc:mysql://localhost/ИМЯ_ВАШЕЙ_БАЗЫ_ДАННЫХ</property>
<property name="hibernate.connection.username">ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ</property>
<property name="hibernate.connection.password">ВАШ_ПАРОЛЬ</property>
<!-- Specifying batch size -->
<property name="hibernate.jdbc.batch_size">50</property>
<!-- List of XML mapping files -->
<mapping resource="Developer.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Developer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="net.proselyte.hibernate.batch.Developer" table="HIBERNATE_DEVELOPERS">
<meta attribute="class-description">This class contains developer details.</meta>
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" type="string"/>
<property name="lastName" column="LAST_NAME" type="string"/>
<property name="specialty" column="SPECIALTY" type="string"/>
<property name="experience" column="EXPERIENCE" type="int"/>
<property name="salary" column="SALARY" type="int"/>
</class>
</hibernate-mapping>
26.4. Шаг 4. Создаём класс DeveloperRunner.java
DeveloperRunner.java
package net.proselyte.hibernate.batch;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class DeveloperRunner {
private static SessionFactory sessionFactory;
public static void main(String[] args) {
sessionFactory = new Configuration().configure().buildSessionFactory();
DeveloperRunner developerRunner = new DeveloperRunner();
System.out.println("Adding 100,000 developer's records to the database...");
developerRunner.addDevelopers();
System.out.println("100,000 developer's records successfully added to the database...");
sessionFactory.close();
}
public void addDevelopers() {
Session session = sessionFactory.openSession();
Transaction transaction = null;
Integer developerId = null;
transaction = session.beginTransaction();
for (int i = 0; i < 100_000; i++) {
String firstName = "First Name " + i;
String lastName = "Last Name " + i;
String specialty = "Specialty " + i;
int experience = i;
int salary = i * 10;
Developer developer = new Developer(firstName, lastName, specialty, experience, salary);
session.save(developer);
if (i % 50 == 0) {
session.flush();
session.clear();
}
}
transaction.commit();
session.close();
}
}
Если всё было сделано правильно, то в результате работы программы получим, примерно, следующий результат:
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7533 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/Hibernate/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-file/4.2.1.RELEASE/spring-integration-file-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/integration/spring-integration-core/4.2.1.RELEASE/spring-integration-core-4.2.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-messaging/4.2.2.RELEASE/spring-messaging-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/retry/spring-retry/1.1.2.RELEASE/spring-retry-1.1.2.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.2.2.RELEASE/spring-tx-4.2.2.RELEASE.jar:/home/proselyte/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4.jar:/home/proselyte/.m2/repository/org/hibernate/hibernate-core/5.1.0.Final/hibernate-core-5.1.0.Final.jar:/home/proselyte/.m2/repository/org/jboss/logging/jboss-logging/3.3.0.Final/jboss-logging-3.3.0.Final.jar:/home/proselyte/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar:/home/proselyte/.m2/repository/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar:/home/proselyte/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/proselyte/.m2/repository/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar:/home/proselyte/.m2/repository/org/jboss/jandex/2.0.0.Final/jandex-2.0.0.Final.jar:/home/proselyte/.m2/repository/com/fasterxml/classmate/1.3.0/classmate-1.3.0.jar:/home/proselyte/.m2/repository/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar:/home/proselyte/.m2/repository/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar:/home/proselyte/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.0.1.Final/hibernate-commons-annotations-5.0.1.Final.jar:/home/proselyte/.m2/repository/javassist/javassist/3.12.1.GA/javassist-3.12.1.GA.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.hibernate.batch.DeveloperRunner Feb 24, 2016 12:54:18 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {5.1.0.Final} Feb 24, 2016 12:54:18 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Feb 24, 2016 12:54:18 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Feb 24, 2016 12:54:18 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final} Feb 24, 2016 12:54:20 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!) Feb 24, 2016 12:54:20 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost/PROSELYTE_TUTORIAL] Feb 24, 2016 12:54:20 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001001: Connection properties: {user=root, password=****} Feb 24, 2016 12:54:20 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator INFO: HHH10001003: Autocommit mode: false Feb 24, 2016 12:54:20 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections INFO: HHH000115: Hibernate connection pool size: 20 (min=1) Wed Feb 24 12:54:20 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. Feb 24, 2016 12:54:20 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect Adding 100,000 developer's records to the database... 100,000 developer's records successfully added to the database... Feb 24, 2016 12:54:41 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost/PROSELYTE_TUTORIAL]
Примерно, вот так будет выглядеть наша таблица
id | FIRST_NAME | LAST_NAME | SPECIALTY | EXPERIENCE | SALARY |
---|---|---|---|---|---|
100061 |
First Name 99988 |
Last Name 99988 |
Specialty 99988 |
99988 |
999880 |
100062 |
First Name 99989 |
Last Name 99989 |
Specialty 99989 |
99989 |
999890 |
100063 |
First Name 99990 |
Last Name 99990 |
Specialty 99990 |
99990 |
999900 |
100064 |
First Name 99991 |
Last Name 99991 |
Specialty 99991 |
99991 |
999910 |
100065 |
First Name 99992 |
Last Name 99992 |
Specialty 99992 |
99992 |
999920 |
100066 |
First Name 99993 |
Last Name 99993 |
Specialty 99993 |
99993 |
999930 |
100067 |
First Name 99994 |
Last Name 99994 |
Specialty 99994 |
99994 |
999940 |
100068 |
First Name 99995 |
Last Name 99995 |
Specialty 99995 |
99995 |
999950 |
100069 |
First Name 99996 |
Last Name 99996 |
Specialty 99996 |
99996 |
999960 |
100070 |
First Name 99997 |
Last Name 99997 |
Specialty 99997 |
99997 |
999970 |
100071 |
First Name 99998 |
Last Name 99998 |
Specialty 99998 |
99998 |
999980 |
100072 |
First Name 99999 |
Last Name 99999 |
Specialty 99999 |
99999 |
999990 |