W tym artykule zapoznasz się z jednym z najpopularniejszych frameworków korporacyjnych dla języka Java i stworzysz swoją pierwszą aplikację przy użyciu Hibernate. Nigdy nie słyszałeś o Hibernacji? Może słyszałeś o tym, ale jeszcze nie stosowałeś? A może próbowałeś zacząć, ale nie udało Ci się? We wszystkich trzech przypadkach zapraszamy na cięcie :) Witam wszystkich! W tym artykule opowiem o głównych cechach frameworka Hibernate i pomogę Ci napisać pierwszą miniaplikację. Do tego potrzebujemy:
Przejdźmy teraz do kodu Java. Utwórz wszystkie niezbędne pakiety i klasy dla projektu. Na początek będziemy potrzebować modeli danych - klas
- Intellij Idea Ultimate Edition;
Pobierz z oficjalnej strony i aktywuj 30-dniową wersję próbną. - PostgeSQL to jeden z najpopularniejszych nowoczesnych systemów zarządzania bazami danych (DBMS);
- Maven (już wbudowany w IDEA);
- Trochę cierpliwości.
Co to jest hibernacja?
Jest to jedna z najpopularniejszych implementacji modelu ORM. Model obiektowo-relacyjny opisuje relacje pomiędzy obiektami oprogramowania a rekordami w bazie danych. Oczywiście funkcjonalność Hibernate jest bardzo szeroka, jednak my skupimy się na najprostszych funkcjach. Nasz cel: stworzyć aplikację CRUD (Create, Read, Update, Delete), która będzie mogła:- Twórz użytkowników (Użytkownik), a także wyszukiwaj ich w bazie po ID, aktualizuj ich dane w bazie, a także usuwaj ich z bazy.
- Przypisz użytkownikom obiekty pojazdów (Auto). Twórz, edytuj, wyszukuj i usuwaj samochody z bazy danych.
- Dodatkowo aplikacja powinna automatycznie usuwać z bazy samochody „osierocone”. Te. Gdy użytkownik zostanie usunięty, wszystkie należące do niego samochody muszą zostać również usunięte z bazy danych.
com.вашНикнейм.javarush
, nie będzie to miało żadnego wpływu na działanie aplikacji. W polu artifactId wybierz dowolną nazwę projektu. Możesz także pozostawić wersję bez zmian. Na ostatnim ekranie wystarczy potwierdzić wprowadzone wcześniej dane. No więc stworzyliśmy projekt, pozostaje tylko napisać kod i sprawić, żeby działał :) Po pierwsze, jeśli chcemy stworzyć aplikację współpracującą z bazą danych, to na pewno nie obejdzie się bez bazy danych! Pobierz stąd PostgreSQL (używam wersji 9). PostgreSQL ma domyślnego użytkownika „postgres”, podczas instalacji będziesz musiał utworzyć dla niego hasło. Nie zapomnij hasła, będzie nam potrzebne później! (Generalnie używanie domyślnej bazy danych w aplikacjach jest złą praktyką, ale aby zmniejszyć ilość hemoroidów, poprzestaniemy na stworzeniu własnej bazy danych). Jeśli nie znasz się na wierszu poleceń i zapytaniach SQL, mamy dobrą wiadomość. Intellij IDEA zapewnia całkiem odpowiedni interfejs użytkownika do pracy z bazą danych. Wygląda to tak: (znajduje się na prawym pasku bocznym IDEA, zakładka Baza danych) Aby utworzyć połączenie, kliknij „+”, wybierz naszego dostawcę (PostgeSQL). Wypełnij pola nazwą użytkownika, bazą danych (oba są postgres) i wprowadź hasło, które ustawiłeś podczas instalacji PostgreSQL. W razie potrzeby pobierz sterownik Postgres, można to zrobić na tej samej stronie. Kliknij „Testuj połączenie”, aby sprawdzić, czy połączenie z bazą danych zostało nawiązane. Jeśli widzisz napis „Udało się”, przechodzimy dalej. Teraz utwórzmy potrzebne nam tabele. Będzie ich dwóch - użytkowników i automatów. Parametry tabeli użytkowników: Należy pamiętać, że identyfikator jest kluczem podstawowym. Jeśli nie wiesz, czym jest klucz podstawowy w SQL, wyszukaj go w Google, to ważne. Ustawienie tabeli autos: W przypadku samochodów musisz skonfigurować klucz obcy - klucz obcy. Połączy nasze stoły. Radzę przeczytać o nim więcej; Mówiąc najprościej, chodzi o zewnętrzną tabelę, w naszym przypadku użytkowników. Jeśli samochód należy do użytkownika o id=1, to w polu user_id tabeli autos będzie miał wartość 1. W ten sposób łączymy użytkowników z ich samochodami w naszej aplikacji. W naszej tabeli autos pole user_id będzie służyć jako klucz obcy. Będzie odnosić się do pola id tabeli użytkowników. W ten sposób stworzyliśmy bazę danych zawierającą dwie tabele. Pozostaje zrozumieć, jak zarządzać nim z poziomu kodu Java. Zaczniemy od pliku pom.xml, w którym musimy zawrzeć niezbędne biblioteki (w języku Maven nazywane są one zależnościami). Wszystkie biblioteki przechowywane są w centralnym repozytorium Mavena. Te z nich, które określisz w pom.xml, możesz wykorzystać w projekcie. Twój pom.xml powinien wyglądać tak: Nic skomplikowanego, jak widzisz. Dodaliśmy tylko 2 zależności - do korzystania z PostgreSQL i Hibernate.
User
i Auto
.
package models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table (name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "name")
private String name;
//nie możesz określić nazwy kolumny, jeśli pasuje ona do nazwy kolumny w tabeli
private int age;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Auto> autos;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
autos = new ArrayList<>();
}
public void addAuto(Auto auto) {
auto.setUser(this);
autos.add(auto);
}
public void removeAuto(Auto auto) {
autos.remove(auto);
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Auto> getAutos() {
return autos;
}
public void setAutos(List<Auto> autos) {
this.autos = autos;
}
@Override
public String toString() {
return "models.User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
package models;
import javax.persistence.*;
@Entity
@Table(name = "autos")
public class Auto {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column (name = "model")
private String model;
//nie możesz określić nazwy kolumny, jeśli pasuje ona do nazwy kolumny w tabeli
private String color;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
public Auto() {
}
public Auto(String model, String color) {
this.model = model;
this.color = color;
}
public int getId() {
return id;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return color + " " + model;
}
}
Jak widać, zajęcia są opatrzone mnóstwem wciąż niejasnych adnotacji. Zacznijmy sobie z nimi radzić. Główną adnotacją dla nas jest @Entity. Poczytaj o tym na Wikipedii i zapamiętaj wszystko, to podstawa. Ta adnotacja umożliwia powiązanie obiektów Java Twojej klasy z bazą danych. Aby klasa była jednostką, musi spełniać następujące wymagania:
- Musi mieć pusty konstruktor (
public
lubprotected
); - Nie może być zagnieżdżony, interfejsowy ani
enum
; - Nie może być
final
i nie może zawieraćfinal
-pól/właściwości; - Musi zawierać co najmniej jedno pole @Id.
- Zawierają niepuste konstruktory;
- Być dziedziczonym i być dziedziczonym;
- Zawierać inne metody i implementować interfejsy.
User
jest bardzo podobna do tabeli users. Istnieją pola id
, name
, age
. Znajdujące się nad nimi adnotacje nie wymagają większego wyjaśnienia: wiadomo już, że @Id jest wskazówką, że pole jest identyfikatorem obiektów tej klasy. Adnotacja @Table nad klasą określa nazwę tabeli, w której zapisywane są obiekty. Zwróć uwagę na komentarz nad polem wieku: jeśli nazwa pola w klasie i tabeli jest taka sama, nie musisz dodawać adnotacji @Column, tak to będzie działać. Jeśli chodzi o „strategię = GenerationType.IDENTITY” wskazaną w nawiasach: istnieje kilka strategii generowania identyfikatora. Możesz to wpisać w Google, ale w ramach naszej aplikacji nie musisz się tym przejmować. Najważniejsze jest to, że identyfikatory dla naszych obiektów będą generowane automatycznie, więc nie ma modułu ustawiającego identyfikator i nie określamy go też w konstruktorze. User
Jednak klasa ta nadal wyróżnia się pod pewnymi względami . Ma listę samochodów! Adnotacja @OneToMany pojawi się nad listą. Oznacza to, że jeden obiekt klasy użytkownika może odpowiadać kilku maszynom. Ustawienie „mappedBY” wskazuje na pole użytkownika klasy Auto
. W ten sposób maszyny i użytkownicy są ze sobą połączeni. Ustawienie orphanRemoval całkiem dobrze tłumaczy się z angielskiego - „usuń sieroty”. Jeśli usuniemy użytkownika z bazy, wszystkie powiązane z nim samochody również zostaną usunięte. Z kolei na zajęciach Auto
zobaczysz pole użytkownika z adnotacją @ManyToOne (wiele Auto może odpowiadać jednemu Użytkownikowi) oraz adnotacją @JoinColumn. Wskazuje, przez którą kolumnę w tabeli autos następuje połączenie z tabelą użytkowników (ten sam klucz obcy, o którym mówiliśmy wcześniej). Po stworzeniu modelu danych przychodzi czas na nauczenie naszego programu wykonywania operacji na tych danych w bazie danych. Zacznijmy od klasy narzędziowej HibernateSessionFactoryUtil. Ma tylko jedno zadanie - stworzyć fabrykę sesji, aby nasza aplikacja mogła współpracować z bazą danych (witaj, wzorzec „Factory!”). Nie może zrobić nic innego.
package utils;
import models.Auto;
import models.User;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateSessionFactoryUtil {
private static SessionFactory sessionFactory;
private HibernateSessionFactoryUtil() {}
public static SessionFactory getSessionFactory() {
if (sessionFactory == null) {
try {
Configuration configuration = new Configuration().configure();
configuration.addAnnotatedClass(User.class);
configuration.addAnnotatedClass(Auto.class);
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
sessionFactory = configuration.buildSessionFactory(builder.build());
} catch (Exception e) {
System.out.println("Wyjątek!" + e);
}
}
return sessionFactory;
}
}
W tej klasie tworzymy nowy obiekt konfiguracyjny Konfiguracja i przekazujemy mu klasy, które powinien postrzegać jako byty - User
oraz Auto
. Zwróć uwagę na metodę configuration.getProperties()
. Jakie inne właściwości? Gdzie? Właściwości to parametry działania hibernacji, określone w specjalnym pliku hibernate.cfg.xml. Plik Hibernate.cfg.xml czyta się tutaj: new Configuration().configure();
Jak widać nie ma w nim nic specjalnego - parametry łączenia się z bazą danych oraz specjalny parametr show_sql. Jest to potrzebne, aby wszystkie zapytania SQL, które hibernacja wykona w stosunku do naszej bazy danych, były wysyłane do konsoli. W ten sposób zobaczysz dokładnie, co robi Hibernacja w danym momencie i pozbędziesz się „magicznego” efektu. Następnie potrzebujemy klasy UserDAO
. (W dobrym tego słowa znaczeniu trzeba programować poprzez interfejsy - utwórz interfejs UserDAO
i osobno go zaimplementuj UserDAOImpl
, ale żeby zmniejszyć ilość kodu, pominę to. Nie rób tego w prawdziwych projektach!). DAO (obiekt dostępu do danych) to jeden z najpopularniejszych wzorców projektowych, „Dostęp do danych”. Jego sens jest prosty – stworzyć w aplikacji warstwę odpowiedzialną jedynie za dostęp do danych i nic więcej. Pobierz dane z bazy danych, zaktualizuj dane, usuń dane - i to wszystko. Przeczytaj więcej o DAO, będziesz ich stale używać w swojej pracy. Co może zrobić nasza klasa UserDao
? Właściwie, jak wszystkie DAO, może pracować tylko z danymi. Znajdź użytkownika po id, zaktualizuj jego dane, usuń go, wyciągnij listę wszystkich użytkowników z bazy lub zapisz nowego użytkownika w bazie - to cała jego funkcjonalność.
package dao;
import models.Auto;
import models.User;
import org.hibernate.Session;
import org.hibernate.Transaction;
import utils.HibernateSessionFactoryUtil;
import java.util.List;
public class UserDao {
public User findById(int id) {
return HibernateSessionFactoryUtil.getSessionFactory().openSession().get(User.class, id);
}
public void save(User user) {
Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
Transaction tx1 = session.beginTransaction();
session.save(user);
tx1.commit();
session.close();
}
public void update(User user) {
Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
Transaction tx1 = session.beginTransaction();
session.update(user);
tx1.commit();
session.close();
}
public void delete(User user) {
Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession();
Transaction tx1 = session.beginTransaction();
session.delete(user);
tx1.commit();
session.close();
}
public Auto findAutoById(int id) {
return HibernateSessionFactoryUtil.getSessionFactory().openSession().get(Auto.class, id);
}
public List<User> findAll() {
List<User> users = (List<User>) HibernateSessionFactoryUtil.getSessionFactory().openSession().createQuery("From User").list();
return users;
}
}
Metody UserDao
są do siebie podobne. W większości z nich za pomocą naszej Fabryki Sesji otrzymujemy obiekt Session (sesję łączącą się z naszą bazą danych), tworzymy pojedynczą transakcję w ramach tej sesji, dokonujemy niezbędnych przekształceń danych, zapisujemy wynik transakcji w bazie danych i zamykamy sesję. Same metody, jak widać, są dość proste. DAO jest „sercem” naszej aplikacji. Nie będziemy jednak tworzyć bezpośrednio DAO i wywoływać jego metod w naszym pliku main()
. Cała logika zostanie przeniesiona do pliku UserService
.
package services;
import dao.UserDao;
import models.Auto;
import models.User;
import java.util.List;
public class UserService {
private UserDao usersDao = new UserDao();
public UserService() {
}
public User findUser(int id) {
return usersDao.findById(id);
}
public void saveUser(User user) {
usersDao.save(user);
}
public void deleteUser(User user) {
usersDao.delete(user);
}
public void updateUser(User user) {
usersDao.update(user);
}
public List<User> findAllUsers() {
return usersDao.findAll();
}
public Auto findAutoById(int id) {
return usersDao.findAutoById(id);
}
}
Usługa to warstwa danych w aplikacji odpowiedzialna za realizację logiki biznesowej. Jeśli Twój program musi wykonać jakąś logikę biznesową, robi to za pośrednictwem usług. Usługa zawiera wewnątrz siebie UserDao
i wywołuje w swoich metodach metody DAO. Może ci się to wydawać powielaniem funkcji (dlaczego nie po prostu wywoływać metod z obiektu dao), ale przy dużej liczbie obiektów i złożonej logice rozbicie aplikacji na warstwy ma ogromne zalety (jest to dobra praktyka, pamiętaj o tej informacji przez dłuższy czas) przyszłość i przeczytaj o „warstwach aplikacji” „). W naszym serwisie logika jest prosta, jednak w realnych projektach metody obsługi będą zawierać znacznie więcej niż jedną linijkę kodu :) Teraz mamy wszystko co potrzebne do działania aplikacji! Stwórzmy main()
dla niego w metodzie użytkownika i maszyny, połączmy je ze sobą i zapiszmy w bazie danych.
import models.Auto;
import models.User;
import services.UserService;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) throws SQLException {
UserService userService = new UserService();
User user = new User("Masha",26);
userService.saveUser(user);
Auto ferrari = new Auto("Ferrari", "red");
ferrari.setUser(user);
user.addAuto(ferrari);
Auto ford = new Auto("Ford", "black");
ford.setUser(user);
user.addAuto(ford);
userService.updateUser(user);
}
}
Jak widać, tabela użytkowników ma swój własny wpis, a tabela autos ma swój własny. Spróbujmy zmienić nazwę naszego użytkownika. Wyczyśćmy tabelę użytkowników i uruchommy kod
import models.Auto;
import models.User;
import services.UserService;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) throws SQLException {
UserService userService = new UserService();
User user = new User("Masha",26);
userService.saveUser(user);
Auto ferrari = new Auto("Ferrari", "red");
user.addAuto(ferrari);
Auto ford = new Auto("Ford", "black");
ford.setUser(user);
user.addAuto(ford);
userService.updateUser(user);
user.setName("Sasha");
userService.updateUser(user);
}
}
Pracuje! Co się stanie, jeśli usuniesz użytkownika? Wyczyśćmy tabelę użytkowników (automaty wyczyści się same) i wykonajmy kod
import models.Auto;
import models.User;
import services.UserService;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) throws SQLException {
UserService userService = new UserService();
User user = new User("Masha",26);
userService.saveUser(user);
Auto ferrari = new Auto("Ferrari", "red");
user.addAuto(ferrari);
Auto ford = new Auto("Ford", "black");
ford.setUser(user);
user.addAuto(ford);
userService.updateUser(user);
user.setName("Sasha");
userService.updateUser(user);
userService.deleteUser(user);
}
}
A nasze tabele są zupełnie puste (zwróć uwagę na konsolę, zostaną tam wyświetlone wszystkie zapytania, które wykonał Hibernacja). Możesz pobawić się aplikacją i wypróbować wszystkie jej funkcje. Przykładowo utwórz użytkownika z maszynami, zapisz go w bazie danych, zobacz jaki jest mu przypisany ID i spróbuj zastosować metodę main()
aby „wyciągnąć” użytkownika z bazy po tym id i wyświetlić listę jego maszyn w konsoli . Oczywiście poznaliśmy tylko niewielką część funkcjonalności Hibernate. Jego możliwości są bardzo szerokie i od dawna jest jednym z branżowych standardów programowania w języku Java. Jeśli chcesz przestudiować to szczegółowo, mogę polecić książkę „Java Persistence API and Hibernate”, którą recenzowałem w jednym z moich poprzednich artykułów. Mam nadzieję, że ten artykuł był przydatny dla czytelników. Jeśli masz jakieś pytania, zadaj je w komentarzach, chętnie odpowiem :) Nie zapomnij też wesprzeć autora w konkursie poprzez „polubienie” go. Albo jeszcze lepiej – „Bardzo mi się to podoba” :) Powodzenia na studiach!
GO TO FULL VERSION