Настройка связи Многие-ко-Многим в Hibernate приложении

Теперь настроим в нашем приложении связь Многие-ко-Многим.

Пусть теперь в нашем приложении будет еще одна новая таблица – "Издательство".

Эта таблица будет связана с ранее созданной таблицей "Книги автора".

Любая книжка может быть напечатана несколькими издательствами и любое издательство может печатать какие-угодно книги.

То есть связь многие книги ко многим издательствам.

Ясное дело, при удалении издательства из таблицы publisher не должны удаляться связанные с этим издательством книги в таблице author_books, поскольку эти книги могут быть связаны и с другими издательствами.

И наоборот тоже – удаление книги не должно приводить к удалению связанных с ней издательств.

Поэтому и в классе таблицы publisher и в классе таблицы author_books необходимо исключить CascadeType=REMOVE.

Сделаем это.

Создадим теперь с помощью sql запроса таблицу publisher в которой будут храниться издательства.

Кто знает базы данных должен знать, что связь многие ко многим между двумя таблицами реализуется с помощью промежуточной таблицы, где есть два столбца – id издательства и id книги. Создадим эту таблицу.

Теперь создадим класс созданной только что таблицы publisher. И настроим связь Многие-ко-Многим между этим классом и классом AuthorBooks через промежуточную таблицу.

package HibernateApps; import java.util.ArrayList; import javax.persistence.*; @Entity @Table(name = “publisher”) public class Publisher { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name=”id”) private int id; @Column(name=”name”) private String publisherName; //Теперь используем аннотацию //@ManyToMany (Многие-ко-многим). //многие Publisher ко многим AuthorBooks. //Важно убрать CascadeType.REMOVE чтобы //при удалении из БД какой-то из книги //не удалялись из БД издательства @ManyToMany(cascade = {CascadeType.DETACH, CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE}) //Теперь настраиваем связи таблицы publisher //с таблицей author_books через промежуточную //таблицу author_books_publisher. @JoinTable( Имя промежуточной таблицы. name = “authours_books_publisher”, //Указываем название атрибута //промежуточной таблицы в котором //хранятся идентификаторы строк //из таблицы publisher. joinColumns=@JoinColumn(name=”publisher_id”), //Указываем название атрибута //промежуточной таблицы в котором //хранятся идентификаторы строк //из таблицы author_books. inverseJoinColumns=@JoinColumn(name=”authorbook_id”)) private List authorbook; public Publisher() { } public Publisher(String publisherName) { this.publisherName = publisherName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getPublisherName() { return publisherName; } public void setPublisherName( String publisherName) { this.publisherName = publisherName; } public List getAuthorbook() { return authorbook; } public void setAuthorbook( List authorbook) { this.authorbook = authorbook; } //Для связывания объекта книги //с объектом издательства просто //добавляем объект книги в List //объекта издательства. Тогда при //добавлении в БД издательства, через //объект издательства добавляются в БД //и книги находящиеся в List //в этом объекте издательства. public void setOneBook( AuthorBooks authorbook){ if (this.authorbook==null) { this.authorbook = new ArrayList<>(); } this.authorbook.add(authorbook); } @Override public String toString() { return “Publisher [id=” + id + “, publisherName=” + publisherName + “, authorbook=” + authorbook + “]”; } }

Теперь в классе таблицы author_books настроим связь Многие-ко-Многим между этим классом и классом Publisher также через промежуточную таблицу.

Связь настраивается таким же образом как и в классе Publisher, только меняем местами publisher_id и authorbook_id

package HibernateApps; import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = “author_books”) public class AuthorBooks { @Id @GeneratedValue( strategy=GenerationType.IDENTITY ) @Column(name=”id”) private int id; @Column(name=”name”) private String bookName; @ManyToOne(cascade ={CascadeType.DETACH, CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn(name=”author_id”) private Author author; //Также используем аннотацию //@ManyToMany (Многие-ко-Многим). //Многие AuthorBooks ко многим Publisher. //Важно убрать CascadeType.REMOVE чтобы //при удалении из БД какого-то из издательств //не удалялись из БД книги напечатанные им. @ManyToMany(cascade ={CascadeType.DETACH, CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable(name=”authorbooks_publisher”, //Связь с таблицей publisher //через промежуточную таблицу //в этом классе настраиваем также //как и в классе Publisher только //как можно увидеть ниже //в joinColumns теперь authorbook_id, //а в inverseJoinColumns //теперь publisher_id. joinColumns= @JoinColumn(name=”authorbook_id”), inverseJoinColumns= @JoinColumn(name=”publisher_id”)) private List publisher; public AuthorBooks () { } public AuthorBooks(String bookName) { this.bookName = bookName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } @Override public String toString() { return “Book [id=” + id + “, bookName=” + bookName + “]”; } }

Пример программы со связью Многие-ко-Многим:

package HibernateApps; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { //Добавим Publisher в sessionfactory SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .addAnnotatedClass(AuthorBooks.class) .addAnnotatedClass(Publisher.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { session.beginTransaction(); //создаем издательства Publisher publisher = new Publisher(“Yakaboo”); Publisher publisher1 = new Publisher(“Vivat”); Publisher publisher2 = new Publisher(“IPIO”); //извлем три книги из БД AuthorBooks authorbook = session.get(AuthorBooks.class,1); AuthorBooks authorbook1 = session.get(AuthorBooks.class,2); AuthorBooks authorbook2 = session.get(AuthorBooks.class,3); //Связываем первое издательство //с первой и третьей книгой. publisher.setOneBook(authorbook); publisher.setOneBook(authorbook2); //Связываем второе издательство //с первой, второй и третьей книгой. publisher1.setOneBook(authorbook); publisher1.setOneBook(authorbook1); publisher1.setOneBook(authorbook2); //Связываем третье издательство //со второй и третьей книгой. publisher2.setOneBook(authorbook1); publisher2.setOneBook(authorbook2); //сохраняем издательства в базу session.save(publisher); session.save(publisher1); session.save(publisher2); session.getTransaction().commit(); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally{ session.close(); } } }

Давайте запустим нашу программу.

Как видим, сначала произошло три select запроса на выборку книг (как мы помним, мы извлекали из БД три книги с помощью get).

Далее произошла вставка (insert) трех издательств в таблицу publisher и потом произошло связывание издательств и книжек посредством вставки идентификаторов в промежуточную таблицу.

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

Типы извлечения данных в Hibernate (fetch types)

LAZY, EAGERэто типы извлечения данных.

Подробнее ниже.

package HibernateApps; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; @Entity //Этот класс с таблицы author @Table(name = “author”) public class Author { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) private int id; @Column(name=”name”) private String authorName; @OneToOne(cascade = {CascadeType.ALL}) @JoinColumn(name=”author_info_id”) private AuthorInfo authorInfo; // LAZY, EAGER – это типы извлечения данных. // Если мы извлекаем автора из БД в объект Author // то извлекаются из БД и его authorbooks. // В прошлом уроке мы видели как это работает. // В этом же уроке мы рассмотрим настройку // того КОГДА извлечение книг будет происходить. // При настроеном EAGER книги автора извлекаються // из БД в список authorbooks, который // в объекте Author СРАЗУ как только мы извлекли // в этот объект Author автора из базы, // но очевидно что это плохо может сказаться // на производительности так как что если мы // хотим в конкретной ситуации извлечь // только автора, а связанные с ним строки в других // таблицах, такие как его книги, пока не извлекать // поскольку в этой конкретной ситуации они нам // пока не нужны. // Решение это LAZY. Здесь все просто. // Данные связанных с автором таблиц извлекаются // только тогда когда они нам нужны. То есть при извлечении // автора из базы извлекается только автор, // а связанные с ним книжки нет, они извлекуться // и добавяться в List объекта извлеченного автора // только когда у этого объекта будет вызван // метод getAuthorBooks() либо любой другой метод, // в котором будет использоваться List с книгами. // При OneToMany и ManyToMany по умолчанию // стоит LAZY, при других EAGER. //Сначала приведем пример с EAGER. @OneToMany(fetch=FetchType.EAGER, mappedBy=”author”, cascade = CascadeType.ALL) private List authorbooks; public Author() { } public Author(String authorName) { this.authorName = authorName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthorName() { return authorName; } public void setAuthorName( String authorName) { this.authorName = authorName; } public AuthorInfo getAuthorInfo() { return authorInfo; } public void setAuthorInfo( AuthorInfo authorInfo) { this.authorInfo = authorInfo; } //добавляем геттер сеттер для списка public List getAuthorBooks(){ return authorbooks; } public void setAuthorBooks( List authorbooks){ this.authorbooks = authorbooks; } @Override public String toString() { return “Author [id=” + id + “, authorName=” + authorName + “, authorInfo=” + authorInfo + “, authorbooks=” + authorbooks + “]”; } }

Пример программы с EAGER типом извлечения данных:

package HibernateApps; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .addAnnotatedClass(AuthorBooks.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { session.beginTransaction(); //Здесь мы выбираем из БД автора //и как говорилось данные связанные //с этим автором в других таблицах //загружаються из БД в обьект author //СРАЗУ поскольку настроено EAGER. Author author = session.get(Author.class,1); //Обьект уже author содержит все книги //связанные с автором. //То есть они извлеклись из БД //СРАЗУ как только мы вызвали get. session.getTransaction().commit(); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

Давайте запустим нашу программу.

Как видим, по вызову метода get произошел запрос select, который извлекает не только автора из БД, а и его книги благодаря тому, что тип извлечения книг был установлен в EAGER.


Настройка типа извлечения данных.

Теперь изменим тип извлечения данных на LAZY.

package HibernateApps; import java.util.List; import javax.persistence.*; @Entity //это класс с таблицы author @Table(name = “author”) public class Author { @Id @GeneratedValue( strategy=GenerationType.IDENTITY ) @Column(name=”id”) private int id; @Column(name=”name”) private String authorName; @OneToOne(cascade=CascadeType.ALL) @JoinColumn(name=”author_info_id”) private AuthorInfo authorInfo; //Теперь приведем пример с LAZY. @OneToMany(fetch=FetchType.LAZY, mappedBy=”author”, cascade = CascadeType.ALL) private List authorbooks; public Author() { } public Author(String authorName) { this.authorName = authorName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthorName() { return authorName; } public void setAuthorName( String authorName) { this.authorName = authorName; } public AuthorInfo getAuthorInfo() { return authorInfo; } public void setAuthorInfo( AuthorInfo authorInfo) { this.authorInfo = authorInfo; } //добавляем геттер сеттер для списка public List getAuthorBooks(){ return authorbooks; } public void setAuthorBooks( List authorbooks){ this.authorbooks = authorbooks; } @Override public String toString() { return “Author [id=” + id + “, authorName=” + authorName + “, authorInfo=” + authorInfo + “, authorbooks=” + authorbooks + “]”; } }

Пример программы с LAZY типом извлечения данных:

package HibernateApp; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(AuthorInfo.class) .addAnnotatedClass(AuthorBooks.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { session.beginTransaction(); //Здесь мы выбираем из БД автора //и как говорилось данные связанные //с этим автором в других таблицах //загружаються из БД в объект author //НЕ СРАЗУ при вызове get поскольку //используеться LAZY. Author author=session.get(Author.class,1); //Объект author пока НЕ содержит книги //связанные с извлеченным только //что автором. То есть они ПОКА //не извлеклись из БД. //Давайте теперь извлечем книги из БД //в объект вызвав метод, в котором //используеться List, в котором должны //храниться книги автора. author.getAuthorBooks(); //Теперь объект автора содержит связанные //с ним в БД книги. session.getTransaction().commit(); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

Давайте запустим нашу программу.

Как видим, по вызову метода get произошел запрос select, который не извлекает книги из БД, а только автора (ну на самом деле на скриншоте еще можно увидеть, что он также извлекает и из таблицы author_info из-за того, что по умолчанию у связей OneToOne стоит тип извлечения данных EAGER).

Это поведение стало возможным благодаря тому, что для коллекции книг был установлен тип извлечения LAZY.

Также на скриншоте видно, что произошёл второй запрос. Он был выполнен при вызове метода getAuthorBooks(). Именно тогда был выполнен дополнительный select, который извлёк книги из базы данных и поместил их в список внутри объекта автора.

В конце можно увидеть содержимое этого списка.

Настройка связи Один-ко-Многим в Hibernate

Теперь настроим в нашем приложении связь 1-ко-Многим.

Создадим еще одну таблицу "Книги авторов", которая будет связана с таблицей author. В одной таблице хранятся авторы, а в другой их книги.

У каждого автора много книг, но у каждой книги только один автор. То есть связь – один автор ко многим книгам.

При удалении автора должны удаляться и его книги из другой таблицы. При удалении книги его автор не должен удаляться. Сделаем это.

Создадим теперь с помощью sql запроса таблицу author_books, в которой будут храниться книги авторов и которая будет связана с таблицей author с помощью внешнего ключа author_id.

Теперь создадим класс созданной только что таблицы author_books. И настроим теперь связь между этим классом и классом Author

Теперь в классе таблицы author настраиваем обратную связь с новым классом.

package HibernateApps; import java.util.List; @Entity //это класс с таблицы author @Table (name = “author”) public class Author { @Id @GeneratedValue( strategy=GenerationType.IDENTITY ) @Column(name=”id”) private int id; @Column(name=”name”) private String authorName; @OneToOne(cascade={CascadeType.ALL}) @JoinColumn(name=”author_info_id”) private AuthorInfo authorInfo; @OneToMany(fetch=FetchType.EAGER, //О том что такое EAGER //в следующем уроке. mappedBy=”author”, //Пускай будут включены //все типы каскадных //операций – CascadeType.ALL. //При удалении //автора пусть удаляются //из БД и все его книги. cascade = CascadeType.ALL) //как видим многие книжки автора //будут записываться в список. private List authorbooks; public Author() { } public Author(String authorName) { this.authorName = authorName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthorName() { return authorName; } public void setAuthorName( String authorName) { this.authorName = authorName; } public AuthorInfo getAuthorInfo() { return authorInfo; } public void setAuthorInfo( AuthorInfo authorInfo) { this.authorInfo = authorInfo; } //добавляем геттер сеттер для списка public List getAuthorBooks(){ return authorbooks; } public void setAuthorBooks( List authorbooks){ this.authorbooks = authorbooks; } @Override public String toString() { return “Author [id=” + id + “, authorName=” + authorName + “, authorInfo=” + authorInfo + “, authorbooks=” + authorbooks + “]”; } }

В программе же создадим объект автора и три объекта книг, свяжем эти книги с объектом автора, и добавим эти все объекты в БД.

Потом извлечем из БД автора в объект автора. Вместе с автором также должны извлечься и связанные с ним книги, которые сохраняться в объекте автора.

Далее удалим одну книгу из БД через объект книги, который храниться в объекте автора. При этом связанный с этой книгой автор не должен удалиться из БД.

package HibernateApps; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { //Добавим AuthorBooks в sessionFactory. SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .addAnnotatedClass(AuthorBooks.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { session.beginTransaction(); //создадим объект Author Author author = new Author(“JJ Rouling”); //и добавим его в таблицу author session.save(author); //Теперь создадим три объекта книги. //Это книги автора, которого мы только //что добавили в БД. AuthorBooks authorbook = new AuthorBooks(“Harry Potter 1”);; AuthorBooks authorbook1 = new AuthorBooks(“Harry Potter 2”); AuthorBooks authorbook2 = new AuthorBooks(“Harry Potter 3”); //поэтому связываем автора с книгами authorbook.setAuthor(author); authorbook1.setAuthor(author); authorbook2.setAuthor(author); //сохраняем книги в базу session.save(authorbook); session.save(authorbook1); session.save(authorbook2); //Закоммитим и начнем //новую сессию и транзакцию. session.getTransaction().commit(); session=sessionFactory.getCurrentSession(); session.beginTransaction(); //Извлечем только что добавленного в БД //автора из БД. Вместе с ним извлекуться //связанные с ним книги и будут храниться //в объекте автора author1. Author author1 = session.get(Author.class, 1); //Можем их увидеть в консоли. System.out.println(author1); //Удалим первую книгу автора из БД. //При удалении книги из БД автор //этой книги из БД не удаляется //как и было задумано. session.delete( author1.getAuthorBooks().get(0)); //Также удалить нужно первую книгу //и из списка книг в объекте author1. author1.getAuthorBooks().remove(0); //Извлечем автора из БД уже с двумя //книгами. Author author2 = session.get(Author.class, 1); //Удалим теперь автора из БД. //При удалении автора из БД //удаляються и его книги из БД //как и было задумано. session.delete(author2); //Извлечем автора из БД еще раз //Не должно получиться так как мы //его только что удалили из БД. Author author3 = session.get(Author.class, 1); session.getTransaction().commit(); //Объект author2 должен содержать //две книги после удаления одной из БД. //Проверим это. System.out.println(author2); //Объект author3 должен быть пуст //после удаления его из БД. //Проверим это. System.out.println(author3); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

И потом удалим автора из БД через объект автора. При этом связанные с этим автором книги тоже должны удалиться из БД.

Давайте запустим нашу программу.

Как видим, произошла вставка в БД четырех объектов, то есть автора и его книг.

После извлечения автора из БД можно увидеть в консоли, что он содержит в себе все книги, которые связаны с ним в БД.

После этого происходит запрос на удаление одной книги из БД через объект книги, который хранился в author1.

Далее можно увидеть, что происходят запросы на удаление из БД остальных книг вместе с автором через объект author2.

После этого можно увидеть содержимое объекта author2. Можно увидеть, что в нем нет одной книги, она была удалена из БД. То есть удаление одной книги автора из БД через объект author1 прошло успешно.

После этого можно увидеть содержимое объекта author3. В нем null поскольку все книги вместе с автором уже были удалены из БД через объект author2.

Двусторонняя связь между сущностями в Hibernate

Связь между объектами таблиц была односторонняя, то есть через объект Author удалялся из БД или сохранялся в БД связанный объект AuthorInfo.

Теперь сделаем наоборот. Так, чтобы через объект AuthorInfo производились какие-то действия в БД со связанным объектом Author.

Теперь уже будет двухсторонняя связь. То есть мы, например, можем совершить выборку из базы с помощью метода get объекта AuthorInfo, и при этом автоматически будет получен из базы связанный с ним объект Author.

Для двусторонней связности объектов связанных строк, необходимо изменить класс AuthorInfo.

package HibernateApps; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Column; import javax.persistence.OneToOne; import javax.persistence.Table; // Это класс с таблицы author_info. @Entity @Table(name = “author_info”) public class AuthorInfo { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) @Column(name = “id”) private int id; @Column(name = “books_written_number”) private int booksWritten; @Column(name = “country”) private String country; // Для двусторонней связанности указываем // в mappedBy каким полем класс Author // связан с данным классом AuthorInfo. @OneToOne(mappedBy = “authorInfo”, cascade = CascadeType.ALL) private Author author; public AuthorInfo() { } public AuthorInfo(int booksWritten, String country) { this.booksWritten = booksWritten; this.country = country; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getBooksWritten() { return booksWritten; } public void setBooksWritten( int booksWritten) { this.booksWritten = booksWritten; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; // В прошлом уроке мы передавали // объект AuthorInfo в сеттер // объекта Author для того чтобы эти // объекты были связаны как связаны // строки этих объектов в БД. // Чтобы можно было так связать // эти объекты наоборот, то есть // в сеттер объекта Author // передать объект Author, // нужно еще и передать // текущий(this) объект AuthorInfo // объекту Author потому что если // этого не сделать то объект Author // не будет знать ничего // о объекте AuthorInfo. // Уточним что класс, в котором // mappedBy не связан с тем, в котором // JoinColumn напрямую. Связанным напрямую // с другим классом можно назвать класс, // в котором JoinColumn. Поэтому обратную // связанность мы скорее имитируем // использованием mappedBy // и конструкциями типа // author.setAuthorInfo(this); author.setAuthorInfo(this); } @Override public String toString() { return “AuthorInfo [id=” + id + “, ” + “booksWritten=” + booksWritten + “, country=” + country + “, author=” + author + “]”; } }

Теперь давайте создадим два объекта, свяжем их теперь уже используя сеттер объекта AuthorInfo и добавим их в БД.

package HibernateApps; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { // create session factory SessionFactory factory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .addAnnotatedClass(AuthorBooks.class) .buildSessionFactory(); // create session Session session = factory.getCurrentSession(); try { session.beginTransaction(); //создадим два объекта и свяжем их //только через сеттер объекта //authorInfo, а не author как //в прошлых уроках AuthorInfo authorInfo = new AuthorInfo(10, “Great Britain”); Author author = new Author(“JJ Rowling”); //связываем authorInfo.setAuthor(author); //при сохранении authorInfo сохраниться //в базу и author поскольку они связаны session.save(authorInfo); AuthorInfo authorInfo1 = session.get(AuthorInfo.class, 1); System.out.println(authorInfo1); //строкой ниже удаляться связанные строки //таблиц через связанные объекты этих строк. session.delete(authorInfo1); AuthorInfo authorInfo2 = session.get(AuthorInfo.class, 1); System.out.println(authorInfo2); session.getTransaction().commit(); } catch (Exception e){ session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

Давайте запустим нашу программу.

Как видим, произошло два запроса на вставку, хотя метод save мы вызывали только над одним объектом actorinfo. Это доказывает, что каскадная PERSIST операция сработала и произошел insert не только объекта authorinfo в таблицу author_info, а и связанного с ним объекта author в таблицу author. То есть двусторонняя связь работает.

Далее с помощью get мы выбрали из таблицы author_info только что положенную туда строку и поместили ее в объект authorinfo1, в консоли можно увидеть содержимое объекта authorinfo1, и как видим он, содержит объект author, то есть опять таки, для выборки связь тоже работает.

После удаления из базы связанных объектов на консоль вывелось null, значит объекты успешно удалились из базы.

Проверим на всякий случай и базу удалились ли обе строки в таблицах:

Настройка связей в Hibernate приложении

В базе данных обычно существуют связанные таблицы.

Все стандартные виды связей (1-к-1, 1-ко-Многим, Многие-Ко-Многим) можно реализовать и между java классами тех таблиц, которые связаны между собой в базе.

Объекты этих связанных классов, очевидно, тоже могут быть связанными. Связанные объекты связанных классов, это связанные строки связанных таблиц.

Благодаря этому, можно сделать так, чтобы при добавлении в таблицу одной строки через объект, одновременно добавлялась в другую таблицу другая строка, благодаря тому, что объекты этих двух строк связаны в программе. То есть в БД добавятся связанные строки.

Также можно сделать так, чтобы при удалении одной строки из таблицы через объект, одновременно удалялась и связанная с этой строкой строка в другой таблице, опять же благодаря тому что объекты этих двух строк связаны в программе.

Также помимо одновременного удаления и добавления есть и другие каскадные операции, которые будут рассмотрены в конце урока.

Давайте с помощью SQL запросов создадим две связанные таблицы – таблицу author и таблицу author_info. В одной таблице хранятся авторы, в другой информация о них.

Между этими таблицами будет тип связи 1-к-1, то есть одной строке таблицы author, соответствует одна строка в таблице author_info. То есть вся информация о конкретном авторе храниться в одной строке другой таблицы.  

Выполним эти SQL запросы:

Теперь создадим класс только что созданной таблицы author_info. В нем пока ничего нового.

package HibernateApps; import javax.persistence.CascadeType; @Entity //это класс с таблицы author_info @Table(name = “author_info”) public class AuthorInfo { @Id @GeneratedValue( strategy=GenerationType.IDENTITY) @Column(name=”id”) private int id; @Column(name=”books_written_number”) private int booksWritten; @Column(name=”country”) private String country; public AuthorInfo() {} public AuthorInfo(int booksWritten, String country) { this.booksWritten = booksWritten; this.country = country; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getBooksWritten() { return booksWritten; } public void setBooksWritten( int booksWritten) { this.booksWritten = booksWritten; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } @Override public String toString() { return “AuthorInfo [id=” + id + “, booksWritten=” + booksWritten + “, country=” + country + “]”; } }

Теперь создадим класс только что созданной таблицы author. В нем мы связываем классы связью 1-к-1.

package HibernateApps; import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Column; import javax.persistence.Table; import javax.persistence.OneToOne; import javax.persistence.JoinColumn; import javax.persistence.CascadeType; //это класс с таблицы author @Entity @Table(name = “author”) public class Author { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = “id”) private int id; @Column(name = “name”) private String authorName; //Помним что в БД у нас настроена связь 1-к-1 //между таблицами author и author_info. //Поэтому мы можем настроить связь мужду //классами этих таблиц – Author и AuthorInfo. //Точнее говоря настраивается //связь между объектом текущего класса //Author и объектом AuthorInfo класса //AuthorInfo, который можно увидеть ниже. //Тоесть связь настраивается между двумя //объектами связанных строк связных таблиц. //Аннотацией @OneToOne настраивается //этот вид связи //Кратко о том что такое cascade //В конце урока. Пока оставим CascadeType.ALL @OneToOne(cascade = CascadeType.ALL) //Указываем каким атрибутом таблица данного //класса связана с таблицей связанного. @JoinColumn(name = “author_info_id”) //Ниже объект связанной таблицы private AuthorInfo authorInfo; public Author() { } public Author(String authorName) { this.authorName = authorName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthorName() { return authorName; } public void setAuthorName(String authorName) { this.authorName = authorName; } public AuthorInfo getAuthorInfo() { return authorInfo; } public void setAuthorInfo(AuthorInfo authorInfo) { this.authorInfo = authorInfo; } @Override public String toString() { return “Author [id=” + id + “, ” + “authorName=” + authorName + “, ” + “authorInfo=” + authorInfo + “]”; } }

Работа со связанными классами. Каскадная PERSIST операция.

Теперь давайте создадим два объекта, свяжем их и добавим их в БД.

package HibernateApps; import java.util.List; public class HibernateApp { public static void main(String[] args) { //Делаем так чтобы sessionFactory анализировал //классы Author и AuthorInfo. SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { //Ниже создаются два объекта созданных классов. Author author = new Author(“JJ Rouling”); AuthorInfo authorInfo = new AuthorInfo( 1, “Great Britain”); //Через сеттер связываем два созданных объекта author.setAuthorInfo(authorInfo); session.beginTransaction(); //Ниже при добавлении в таблицу author одной //строки через объект author, добавляется //в таблицу author_info другая строка, //благодаря тому что объекты этих двух //строк мы только что связали. //Это называется каскадная PERSIST операция, //тоесть когда сохраняется один объект в базу //сохраняется и связанный с ним. session.save(author); session.getTransaction().commit(); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

Давайте запустим нашу программу.

Как видим, произошло два запроса на вставку, хотя метод save мы вызывали только над одним объектом actor. Это доказывает что каскадная PERSIST операция сработала и произошел insert не только объекта author в таблицу author, а и связанного с ним объекта authorinfo в таблицу author_info.

Как видим в обе таблицы успешно добавились объекты.


Работа со связанными классами. Каскадная REMOVE операция.

Теперь продемонстрируем каскадную REMOVE операцию.

package HibernateApps; import java.util.List; public class HibernateApp { public static void main(String[] args) { //Делаем так чтобы sessionFactory анализировал //классы Author и AuthorInfo. SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { session.beginTransaction(); //Давайте извлечем из таблицы actor строку //которую мы только что туда добавили. Author author = session.get(Author.class, 1); //Вместе с извлеченной строкой из таблицы author //извлекается и связанная с ним строка из //таблицы author_info. Тоесть в объекте author //строки из таблицы author //уже будет присутствовать объект связанной //с этой строкой строки из таблицы author_info. System.out.println(author); //Ниже при удалении одной строки //из таблицы author через объект author, //одновременно удаляется и связанная с этой //строкой строка из таблицы author_info, //опять же благодаря тому, //что объекты этих двух строк связаны в программе. //Это называется каскадная REMOVE операция, //тоесть когда удаляется один объект в базе //удаляется и связанный с ним из базы. session.delete(author); session.getTransaction().commit(); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

Давайте запустим нашу программу.

Как видим, в объекте author присутствует объект authorInfo, который соответствует связанной строке из таблицы author_info.

Далее можно увидеть, что произошло два запроса на удаление, хотя метод delete мы вызывали только над одним объектом actor. Это доказывает, что каскадная REMOVE операция сработала и произошел delete не только объекта author в таблице author, а и связанного с ним объекта в таблице author_info.

Связанные строки успешно удалились из обеих таблиц.


О каскадных операциях

Только что мы рассмотрели две каскадные операции PERSIST(добавление связанных объектов в БД), REMOVE(удаление связанных объектов из БД).

Но есть и другие – MERGE, DETACH и REFRESH.

Детали их работы и настройки покажем на примере.

package HibernateApps; import java.util.List; @Entity //это класс с таблицы author @Table(name = “author”) public class Author { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) @Column(name=”id”) private int id; @Column(name=”name”) private String authorName; //CascadeType.ALL – все каскадные операции разрешены. //Подробное пояснение типов cascade приведено ниже @OneToOne(cascade={CascadeType.ALL}) @JoinColumn(name=”author_info_id”) private AuthorInfo authorInfo; public Author() { } public Author(String authorName) { this.authorName = authorName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthorName() { return authorName; } public void setAuthorName(String authorName) { this.authorName = authorName; } public AuthorInfo getAuthorInfo() { return authorInfo; } public void setAuthorInfo(AuthorInfo authorInfo) { this.authorInfo = authorInfo; } @Override public String toString() { return “Author [id=” + id + “, ” + “authorName=” + authorName + “, authorInfo=” + authorInfo + “]”; } }

CascadeType.ALL значит, что все каскадные операции разрешены. Вместо CascadeType.ALL можно было написать так: cascade={CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH} и это было бы то же самое что CascadeType.ALL.

Любой из каскадирований можно убирать или добавлять в этих скобочках, то есть если убрать CascadeType.REMOVE, то каскадная операция REMOVE работать больше не будет со связанными объектами Author и AuthorInfo. При удалении объекта Author из БД, связанный с ним объект AuthorInfo из БД удаляться не будет.

Кратко рассмотрим каждую каскадную операцию на примере связи 1-к-1 объектов Author и AuthorInfo.

DETACH – если объект класса Author внезапно перестает принадлежать сессии, тогда и связанный с ним объект класса AuthorInfo тоже перестает ей принадлежать. Это можно сделать с помощью функции session.detach().

MERGE – если через объект класса Author происходит обновление строки в таблице author, то тогда через объект класса AuthorInfo, который связан с этим объектом Author, происходит и обновление связанной с этой строкой строки в таблице author_info. Это можно сделать с помощью функции session.save().

PERSIST – если через объект класса Author происходит сохранение новой строки в таблице author, то тогда через объект класса AuthorInfo, который связан с этим объектом Author, тоже происходит сохранение новой строки в таблицу author_info. Это можно сделать с помощью функции session.save().

REMOVE – если через объект класса Author происходит удаление строки в таблице author, то тогда через объект класса AuthorInfo, который связан с этим объектом Author, происходит и удаление связанной с этой строкой строки в таблице author_info. Это можно сделать с помощью функции session.delete().

REFRESH – если в программе происходит обновление объекта класса Author (имеется в виду перезвлечение строки таблицы в объект. Тоесть, происходит обновление объекта в программе, не в БД), то происходит и обновление (перевзвлечение) объекта, который связан с объектом класса AuthorInfo. Это можно сделать с помощью функции session.refresh().

Search Icon

Важно помнить, что по умолчанию cascade не задан, то есть он ничего не содержит. Поэтому нужно явно устанавливать нужный тип cascade между таблицами.

HQL запросы в Hibernate. Удаление данных (Delete)

Если извлечь строку таблицы в объект, а потом применить метод delete на этот объект, то строка, которую мы извлекали удалиться в базе данных. Таким образом с помощью метода delete можно удалять одну строку таблицы.

package HibernateApps; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { SessionFactory sessionfactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Book.class) .buildSessionFactory(); Session session = sessionfactory.getCurrentSession(); try { //Delete одной строки таблицы с помощью delete session = sessionfactory.getCurrentSession(); session.beginTransaction(); //Извлекаем строку с ключем 2 в объект Book Book Hobbitbook = session.get(Book.class, 2); //С помощью delete удаляем строку с ключем 2 //из таблицы через объект Hobbitbook. session.delete(Hobbitbook); //Извлекаем все книги в таблице List books = session.createQuery( “from Book”).getResultList(); session.getTransaction().commit(); //выведем их showBooks(books); } catch (Exception e) { //откатываем изменения в бд //если при транзакции случилась ошибка session.getTransaction().rollback(); e.printStackTrace(); } finally{ //по окончании работы с сессией закрываем ее session.close(); } } private static void showBooks(List books) { for(Book tempBook : books) { System.out.println(tempBook.toString()); } } }

Давайте запустим нашу программу.

Видим, что в таблице осталась одна строка, строку с ключом 2 мы успешно удалили.


Delete с помощью запроса

Также можно удалять строки таблицы запросом. Благодаря чему мы можем удалять сразу несколько строк таблицы одним запросом.

package HibernateApps; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { SessionFactory sessionfactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Book.class) .buildSessionFactory(); Session session = sessionfactory.getCurrentSession(); try { //Delete нескольких строк таблицы запросом. session = sessionfactory.getCurrentSession(); session.beginTransaction(); //Добавим еще одну книгу в таблицу session.save(new Book(“LOTR”)); //Удаляем строки с id равными 1 и 3. //То есть ту которая была в таблице и //новую добавленную. session.createQuery(“delete from Book where id=1″ +” or id=3″).executeUpdate(); //Извлечем все книги в таблице List books = session.createQuery( “from Book”).getResultList(); session.getTransaction().commit(); //проверим содержимое books showBooks(books); } catch (Exception e) { //откатываем изменения в бд //если при транзакции случилась ошибка session.getTransaction().rollback(); e.printStackTrace(); } finally{ //по окончанию работы с сессией закрываем ее session.close(); } } private static void showBooks(List books) { for(Book tempBook : books) { System.out.println(tempBook.toString()); } } }

Давайте запустим нашу программу.

Как видим, результатом select запроса оказался пустой список, раз ничего не вывелось в конце. Значит обе строки таблицы были удалены успешно.

HQL запросы в Hibernate. Обновление данных (Update)

Если мы извлекли из таблицы строку в объект, а потом изменили его с помощью например сеттера, то после коммита транзакции он измениться и в базе.

package HibernateApps; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { SessionFactory sessionfactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Book.class) .buildSessionFactory(); Session session = sessionfactory.getCurrentSession(); try { session = sessionfactory.getCurrentSession(); session.beginTransaction(); //извлекаем строку с ключем 2 в объект Book Book LOTRBook = session.get(Book.class, 2); //меняем имя книги с помощью сеттера LOTRBook.setBookName(“Lord Of The Rings”); //и сразу коммитим. после этого строка //таблицы с ключем 2 тоже должна измениться session.getTransaction().commit(); //создаем новую сессию и транзакцию session = sessionfactory.getCurrentSession(); session.beginTransaction(); //извлекаем еще раз Book LOTRBook1 = session.get(Book.class, 2); //выведем извлеченную книгу на консоль System.out.println(LOTRBook1.toString()); session.getTransaction().commit(); } catch (Exception e) { //откатываем изменения в бд //если при транзакции случилась ошибка session.getTransaction().rollback(); e.printStackTrace(); } finally{ //по окончании работы с сессией закрываем ее session.close(); } } private static void showBooks(List books) { for(Book tempBook : books) { System.out.println(tempBook.toString()); } } }

Давайте запустим нашу программу.

В последней строке видим название 2 книги изменилось после первого коммита и в базе. То есть мы просто меняем объект, который мы предварительно извлекли из базы и после коммита он меняется и в базе.

Довольно удобно, в sql пришлось бы писать команду update


Update таблицы с помощью запроса

Также можно обновлять строки таблицы запросом. То есть нам, например, может понадобиться обновить несколько строк таблицы и не очень будет удобно доставать эти строки, помещать их в объекты изменять их, а потом коммитить, чтобы они изменились в базе, как в прошлом примере. Проще сделать один запрос.

package HibernateApps; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { SessionFactory sessionfactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Book.class) .buildSessionFactory(); Session session = sessionfactory.getCurrentSession(); try { //Update нескольких строк таблицы запросом. session = sessionfactory.getCurrentSession(); session.beginTransaction(); // устанавливаем значение bookName равным Hobbit // в строках таблицы где id равен 1 и 2 session.createQuery(“update Book set bookName='” + “Hobbit’ where id=1 or id=2”).executeUpdate(); //извлечем строки таблицы для проверки List books3 = session.createQuery( “from Book”).getResultList(); session.getTransaction().commit(); //проверим содержимое books3 showBooks(books3); } catch (Exception e) { //откатываем изменения в бд //если при транзакции случилась ошибка session.getTransaction().rollback(); e.printStackTrace(); } finally { //по окончании работы с сессией закрываем ее session.close(); } } private static void showBooks(List books) { for(Book tempBook : books) { System.out.println(tempBook.toString()); } } }

Давайте запустим нашу программу.

Как видим, обе строки таблицы были изменены успешно с помощью Update запроса.

Обзор языка запросов HQL в Hibernate

В hibernate используется HQL, а не SQL.

HQL работает с таблицей через класс, который связан с ней.

То есть в запросе к БД мы указываем не название таблицы, и ее аттрибутов, а название класса и его полей. Таким образом HQL является более объектно-ориентированным


Работа с HQL.

Давайте сделаем пару запросов на языке HQL.

В принципе он очень схож с SQL, поэтому трудностей для его понимания возникать не должно у тех кто знаком с SQL.

package HibernateApps; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateApp { public static void main(String[] args) { SessionFactory sessionfactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Book.class) .buildSessionFactory(); Session session = sessionfactory.getCurrentSession(); try { session = sessionfactory.getCurrentSession(); session.beginTransaction(); // Добавим еще одну книгу в таблицу session.save(new Book(“LOTR”)); System.out.println(“--------------------”); // Ниже происходит запрос выборки данных // из таблицы books через класс Book. // Вместо select * from books будет просто // from Book (где Book – имя класса). List books = session.createQuery( “from Book”) .getResultList(); System.out.println(“--------------------”); // Извлечем теперь две конкретных книжки // для демонстрации умного ключевого выражения. // Здесь bookName — поле класса, вместо атрибута // name таблицы. // Как видим, язык во многом похож на SQL List books2 = session.createQuery( “from Book b where ” + “b.bookName=’Harry Potter’ ” + “or b.bookName=’LOTR'”) .getResultList(); System.out.println(“---------------------“); session.getTransaction().commit(); // Проверим содержимое списков books и books2, // в которых хранятся результаты запросов. // Для этого создана функция showBooks ниже. // Она просто проходит циклом по List. showBooks(books); System.out.println(“---------------------“); showBooks(books2); } catch (Exception e) { // Откатываем изменения в БД, // если при транзакции случилась ошибка session.getTransaction().rollback(); e.printStackTrace(); } finally { // По окончании работы с сессией закрываем ее session.close(); } } private static void showBooks(List books) { for (Book tempBook : books) { System.out.println(tempBook.toString()); } } }

Проверка работы Hibernate приложения.

Давайте запустим нашу программу.

Как видим, произошло три запроса. Один добавляет книгу в таблицу, Другие два производят выборку книг из таблицы.

Как видим, результаты запросов выборки были успешны. Первый запрос на выборку всех книг выбрал все книги и второй запрос на выборку двух конкретных книг выбрал эти две книги.

Введение в ORM с использованием Hibernate

Hibernate реализует ORM-модель построения приложений.

Благодаря ORM можно сохранить объект в базу данных.

При сохранении какого-либо объекта в базу данных, объект будет превращен в строку таблицы базы данных.

Значения же полей этого объекта будут значениями аттрибутов этой строки таблицы. То есть поля объекта ассоциированы с аттрибутами таблицы, а класс объекта ассоциирован с конкретной таблицей в БД.

То есть если, например, есть класс "Пользователь" с определенными полями – имя, фамилия, возраст и мы хотим поместить объект этого класса в БД, то там должна быть соответствующая этому классу таблица с аттрибутами имя, фамилия, возраст, в которой и будут храниться объекты класса "Пользователь".

Search Icon

Hibernate также помогает сократить много jdbc кода (внутри Hibernate выполняется весь jdbc код, который нам пришлось бы писать) и избавиться от low-level SQL запросов.


Создание Hibernate проекта

Для начала необходимо скачать  Hibernate ORM.

Это можно сделать по ссылке https://hibernate.org/orm/releases/5.6/.

Ищем версию 5.6.5 и скачиваем.

Также необходимо скачать файлики dom4j-2.1.3.jar и mysql-connector-j-8.0.32.jar. Их можно скачать на mavenrepository.com.

Далее создаем обычный Java проект с main. Для того чтобы демонстрировать азы Hibernate простого main проекта будет достаточно.

Чтобы создать проект для начала сменим режим нашей ide, для этого переходим Window -> Perspective -> Open Perspective -> Other -> Java.

Теперь создаем проект. Создается он по пути File -> New -> Java Project. Даем имя проекту и нажимаем Finish.

Теперь в созданном проекте создаем папку lib  и закидываем в нее все файлы из папки required в архиве Hibernate Orm. Также закидываем туда другие два jar, которые были скачаны отдельно.  

Теперь необходимо вручную добавить их в classpath. Для этого кликаем правой кнопкой мыши по проекту и выбираем -> properties -> Java Build Path -> Add Jars и выбираем все jar файлы в папке lib. Теперь можем писать код.      

Структура проекта такая:

Как видим, в проект также были добавлены три файлика. Их разберем далее.


Работа с Hibernate

Давайте для начала sql запросом создадим простую таблицу "books" с одним аттрибутом "name". Ну и аттрибутом id само собой.

Мы создали таблицу. Теперь давайте же создадим класс Books, который будет связан с созданной таблицей. Связь происходит с помощью аннотаций. Имя этот класс может иметь любое и поля в нем тоже могут иметь любые имена. Нам главное связать класс с таблицей и поля класса с аттрибутами таблицы.

package HibernateApps; import javax.persistence.Column; // Класс, который будет связан с таблицей в БД // Помечается аннотацией @Entity @Entity // Связываем этот класс с таблицей books // Аннотацией @Table. // Передаем в нее имя таблицы. @Table(name = “books”) public class Book { // Свяжем поле id класса с ключевым атрибутом // в таблице. // Для этого для начала помечаем // его аннотацией @Id. @Id // Аннотацией @GeneratedValue можем указать, // каким образом будут генерироваться ключи // в таблице. // identity – значение по умолчанию. // Означает, что мы оставляем генерацию значений // ключей на выбор используемой стратегии. // То есть БД будет автоматически // инкрементировать значение ключа // при вставке новой записи в таблицу. // Также, например, можем создать собственную // стратегию генерации и указать здесь, // но мы оставим identity. @GeneratedValue(strategy = GenerationType.IDENTITY) // С помощью Column связываем поля // класса с соответствующими столбцами таблицы, // передавая в аннотацию имена этих столбцов. @Column(name = “id”) private int id; @Column(name = “name”) private String bookName; public Book() { } public Book(String bookName) { super(); this.bookName = bookName; } // Также обязательно создаем геттеры и сеттеры public int getId() { return id; } public void setId(int id) { this.id = id; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } // И переопределим метод toString, чтобы он // выводил содержимое объекта класса. // Пригодится. @Override public String toString() { return “Book [id=” + id + “, bookName=” + bookName + “]”; } }

Теперь нам нужно настроить подключение Hibernate к maysql и базе данных.

com.mysql.jdbc.Driver jdbc:mysql://localhost/storage org.hibernate.dialect.MySQL8Dialect root 07998MSD org.hibernate.dialect.MySQLDialect thread true true

Теперь собственно перейдем к созданию Hibernate программы. В ней мы просто создаем объект класса Books, сохраняем его в таблицу books в виде строки, потом эту же строку, которую мы туда сохранили извлекаем из таблицы в новый объект.

package HibernateApps; import java.util.List; public class HibernateApp { public static void main(String[] args) { // Объект SessionFactory анализирует конфигурационный // файл, совершает подключение к БД, анализирует классы // с аннотациями и создает объекты Session. // Session объект является оберткой над ОДНИМ // jdbc подключением к базе, то есть можно создать // несколько сессий, а значит несколько подключений. // Этот объект используется для взаимодействия с базой. // Короткоживущий объект создается для каждой транзакции. // То есть создали Сессию -> совершили действия с базой // в пределах транзакции -> закоммитили транзакцию -> // объект сессии умер. // Как видим, мы передаем объекту SessionFactory имя файла // конфигурации и имя класса с аннотациями для связи с БД. SessionFactory sessionfactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Book.class) .buildSessionFactory(); Session session = sessionfactory.getCurrentSession(); try { // Создадим объект класса Book, который // как мы помним, связан с таблицей books // поэтому мы можем добавить этот объект // в эту таблицу как строку таблицы. Book book = new Book(“Harry Potter”); // Выведем на консоль id. Он пока ноль System.out.println(book.toString()); // В Hibernate мы работаем с транзакциями // точнее с последовательностями запросов, // которые можно откатить, поэтому начинаем // транзакцию с помощью beginTransaction. session.beginTransaction(); // Сохраняем объект в таблицу, связанную // с его классом с помощью аннотаций. session.save(book); // Сохраняем изменения в базе с помощью commit. session.getTransaction().commit(); // После коммита id объекта book уже будет // не ноль. После коммита полю объекта book, // помеченному как ключ аннотацией @Id, // присваивается автоинкрементированное // базой значение. System.out.println(book.toString()); // id уже 1 // Новая сессия создается для каждой новой транзакции session = sessionfactory.getCurrentSession(); session.beginTransaction(); // С помощью get можем извлечь объект book // из базы в новый объект. // Передаем в метод get имя класса объекта, // который мы извлекаем, и ключ строки извлекаемого объекта // в таблице. Book harrypotterbook = session.get(Book.class, book.getId()); session.getTransaction().commit(); // Мы извлекли ранее созданный объект book // в новый объект harrypotterbook, значит id // у него тоже будет 1 System.out.println(harrypotterbook.toString()); } catch (Exception e) { // Откатываем изменения в БД // если при транзакции случилась ошибка session.getTransaction().rollback(); e.printStackTrace(); } finally { // По окончании работы с сессией закрываем ее session.close(); } } }

Давайте запустим нашу программу. Для запуска main приложения в eclipse просто достаточно нажать зеленую кнопку в панели сверху.

Первая строка это первый вывод на консоль в программе с помощью ToString. Как уже было сказано, id у объекта пока ноль.

Далее insert запросом происходит добавление объекта в базу.

Второй вывод на консоль содержимого объекта показывает, что после вызова save над объектом, который мы сохраняли в БД, поле id этого объекта теперь имеет значение. То есть метод save записал в поле id объекта book значение.

Далее происходит запрос на выборку только что добавленной строки в таблицу в новый объект.

В последнем выводе на консоль видим, что мы успешно извлекли из таблицы ее строку в новый объект содержимое, которого мы выводим.