W tym artykule postaram się pomóc odnaleźć się w gąszczu możliwości jakie daje Hibernate. Wyjaśnię, gdzie i jak wykorzystać tytułowe SessionFactory, EntityManager oraz JpaRepository/CrudRepository. Osoby uczące się Hibernate nieraz szukając rozwiązań w internecie natrafiają na morze możliwości.
Wstęp
Zaczynając swoją przygodę z Hibernate wiele osób gubi się w morzu możliwości, które oferuje Hibernate. Zacznę od tego, że Hibernate jest jedną z implementacji JPA – Java/Jakarta Persistence API. Popularne implementacje JPA: Hibernate, EclipseLink, OpenJPA oraz DataNucleus.
Naukę Hibernate – jak większość frameworków – warto zacząć od oficjalnej dokumentacji. Już na stronie głównej widzimy bardzo wymowny banner “More than an ORM, discover the Hibernate galaxy.” W skład Hibernate wchodzą np.: Hibernate Search oraz Hibernate Validator (używany w Spring Framework – Spring MVC).
Sposoby działania z Hibernate – 4 różne
Z oficjalnej dokumentacji, a dokładnie z części “Getting Started with Hibernate“, dowiadujemy się, że Hibernate można używać na trzy sposoby, ja dorzucę czwarty.
Getting Started with Hibernate:
- Using native Hibernate APIs.
- Using JPA-standard APIs.
- Using Envers.
- (dorzucam) Using Spring DataJPA.
Każdy z powyższych punktów szczegółowo opiszę w dalszej części.
Wyjaśnienie podstawowych elementów
Zanim zagłębimy się sposoby używania Hibernate wyjaśnijmy podstawowe pojęcia. Umieszczę techniczne opisy oraz moją własną interpretację.
API
An application programming interface (API) is a way for two or more computer programs to communicate with each other. It is a type of software interface, offering a service to other pieces of software. Źródło Wikipedia.
Moja interpretacja: API, to zbiór interfejsów programistycznych, których cechą jest, to, że wskazują, że coś można zrobić, ale nie mówią jak, to zrobić. Np. JPA “mówi że” można utrwalać obiekty Java, ale nie wskazuje jak utrwalać, to konkretna implementacja JPA np. Hibernate zawiera kod zapisujący do bazy danych.
JPA
Jakarta Persistence (JPA; formerly Java Persistence API) is a Jakarta EE application programming interface specification that describes the management of relational data in enterprise Java applications. Źródło Wikipedia.
Moja interpretacja: JPA zawiera zbiór adnotacji, za pomocą, których można stworzyć definicję klasy Java, która odzwierciedla tabelę w bazie danych – encja,@Entity. Dostarcza zbiór interfejsów z metodami do utrwalania danych –EntityManager, np. metodapersist().
ORM
Object–relational mapping (ORM, O/RM, and O/R mapping tool) in computer science is a programming technique for converting data between a relational database and the heap of an object-oriented programming language. Źródło Wikipedia.
Moja interpretacja: Mechanizm mapowania klas Java na tabele w bazie danych – na podstawie definicji – encji,@Entity– zawartej w klasie Java.
Czym jest Hibernate?
Zacznę od mojej własnej interpretacji. Hibernate, to ORM, który umożliwia mapowanie klas Java na tabele bazodanowe. Hibernate jest jedną z implementacji JPA. Warto pamiętać, że Hibernate w swojej implementacji korzysta z Javowego JDBC. Znajomość Hibernate nie zwalnia ze znajomości JDBC oraz SQL.
Na oficjalnej stronie Hibernate przeczytamy: “Hibernate ORM. Domain model persistence for relational databases.” Na temat Hibernate ORM przeczytamy: “Object/Relational Mapping. Hibernate ORM enables developers to more easily write applications whose data outlives the application process. As an Object/Relational Mapping (ORM) framework, Hibernate is concerned with data persistence as it applies to relational databases (via JDBC).”
Dlaczego ORM?
Dlaczego mapujemy klasy Java na tabele w bazie danych? Pierwsze, co przychodzi na myśl, to zapisanie stanu obiektów Java do bazy danych. Jak, to możliwe, że klasy Java można “zapisać w bazie daych”?
Pierwsza kwestia, to właśnie ORM – więcej na ten temat można przeczytać “Hibernate ORM – What is Object/Relational Mapping?” Druga kwestia, to, że klasy Java są bardzo podobne do tabel bazodanowych. Jak, to? Klasy Java są jak tabele? Wyjaśnienie poniżej.
Język Java jest obiektowy, między klasami są zależności, asocjacje, połączenia. Natomiast bazy danych, z których korzysta Hibernate ORM są bazami relacyjnymi, między tabelami są relacjie z wykorzystaniem klucza głównego oraz klucza obcego.
public class Address {
private Long id;
private String street;
}
CREATE TABLE ADDRESSES (
ID BIGINT PRIMARY KEY,
STREET VARCHAR(255)
)
Powyższa klasa Java Address oraz tabela bazodanowa ADDRESSES wyglądają podobnie, jeżeli używają “prostych typów”, sprawa wygląda inaczej, gdy klasa ma zależności, a w tabeli pojawia się relacje.
public class Client {
private Long id;
private String name;
private Address address;
}
CREATE TABLE CLIENTS (
ID BIGINT PRIMARY KEY,
NAME VARCHAR(255),
ADDRESS_ID BIGINT,
CONSTRAINT FK_CLIENTS_ADDRESSES
FOREIGN KEY (ADDRESS_ID)
REFERENCES ADDRESSES(ID)
)
Powyższa klasa Java Client zawiera zależność do innej klasy Address. Natomiast w tabeli bazodanowej CLIENTS pojawia się klucz obcy – FOREIGN KEY – do klucza głównego – PRIMARY KEY – w tabeli ADDRESSES. Klucz obcy o nazwie FK_CLIENTS_ADDRESSES, a klucz główny dla kolumny ID.
Jak używać Hibernate
Całość kodu można znaleźć na moim koncie GitHub
https://github.com/juniorjavadeveloper-pl/hibernate-examples
Oficjalna dokumentacja – Getting Started with Hibernate
https://docs.jboss.org/hibernate/orm/6.4/quickstart/html_single/
Mapowanie Java na TABLE – @Entity
Konfiguracja Hibernate oraz sposoby korzystania z niego, to jedno, ale efekt końcowy jest taki, że zapisujemy coś w relacyjnej bazie danych. Musimy odwzorować, mapować klasę języka Java na tabele bazodanowe, do tego służą nam encje, klasy oznaczone adnotacją @Entity. Takie klasy stanowią pomost, szablon, definicję dla Hibernate, który wykona za nas całą pracę, którą do tej pory musieliśmy samodzielnie robić za pomocą czystego JDBC.
@Entity
@Table(name = "ZOO")
public class Animal {
@Id
@GeneratedValue
private Long id;
@Column(name = "BIRTH_DATE", nullable = false)
private Date date;
private String name;
public Animal() {
}
}
Który ze sposobów użycia Hibernate był pierwszy? Odpowiedź ChatGPT
Co było pierwsze jajko czy kura? Podobne pytanie można sobie zadać w związku z Hibernate.
Który ze sposobów użycia Hibernate był pierwszy?
Hibernate był pierwszym rozwiązaniem jako samodzielny framework ORM z natywnym API (2001 r.). Specyfikacja JPA została wprowadzona (2006 r.) później w celu ujednolicenia ORM w Javie, a Hibernate ostatecznie zintegrował obsługę JPA (2010 r.). Następnie w ramach projektu Spring Data wprowadzono Spring Data JPA (2011 r.), aby uprościć dostęp do danych w aplikacjach Spring korzystających z JPA.
- 2001 r. – Native Hibernate API.
- 2006 r. – JPA specification.
- 2010 r. – JPA-standard API w Hibernate.
- 2011 r. – Spring Data JPA.
Native Hibernate API – SessionFactory
Jedną z dostępnych możliwości jest Native Hibernate API, czyli natywne, naturalne/rdzenne API Hibernate. W telegraficznym skrócie, aby korzystać z Native Hibernate API potrzebujemy SessionFactory – Hibernate Native Bootstrapping.
SessionFactory
Javadoc: The main contract here is the creation of Session instances. Usually an application has a single SessionFactory instance and threads servicing client requests obtain Session instances from this factory.
The internal state of a SessionFactory is immutable. Once it is created this internal state is set. This internal state includes all of the metadata about Object/Relational Mapping.
Implementors must be threadsafe.
public class HibernateNativeBasicConfigurationTest {
private SessionFactory sessionFactory;
@BeforeEach
void setUp() {
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.configure()
.build();
try {
sessionFactory = new MetadataSources(serviceRegistry)
.buildMetadata()
.buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
StandardServiceRegistryBuilder.destroy(serviceRegistry);
}
}
@AfterEach
void tearDown() {
if (sessionFactory != null) {
sessionFactory.close();
}
}
}
Kod na GitHub -> HibernateNativeBasicConfigurationTest
Poza SessionFactory potrzebujemy jeszcze konfiguracje Hibernate np.: w postaci pliku XML – hibernate.cfg.xml. Poniżej fragment konfiguracji z linkiem do GitHub z pełną zawartością pliku konfiguracyjnego.
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- https://github.com/juniorjavadeveloper-pl/hibernate-examples/blob/master/src/test/resources/hibernate.cfg.xml -->
</session-factory>
</hibernate-configuration>
JPA-standard API – EntityManager
Kolejną opcją jest JPA-standard API, czyli implementacja specyfikacji Java/Jakarta Persistence API w Hibernate. W telegraficznym skrócie, aby korzystać z JPA-standard API potrzebujemy EntityManager – Hibernate Jakarta Persistence Bootstrapping.
EntityManager
Javadoc: Interface used to interact with the persistence context.
An EntityManager instance is associated with a persistence context. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed. The EntityManager API is used to create and remove persistent entity instances, to find entities by their primary key, and to query over entities.
The set of entities that can be managed by a given EntityManager instance is defined by a persistence unit. A persistence unit defines the set of all classes that are related or grouped by the application, and which must be colocated in their mapping to a single database.
public class HibernateJpaBasicConfigurationTest {
private EntityManagerFactory entityManagerFactory;
@BeforeEach
void setUp() {
entityManagerFactory = Persistence.createEntityManagerFactory(
"pl.juniorjavadeveloper.examples.hibernate.basic.configuration.pu");
}
@AfterEach
void tearDown() {
if (entityManagerFactory != null) {
entityManagerFactory.close();
}
}
}
Kod na GitHub -> HibernateJpaBasicConfigurationTest
Poza EntityManager potrzebujemy jeszcze konfiguracje Java/Jakarta Persistence np.: w postaci pliku XML – persistence.xml. Poniżej fragment konfiguracji z linkiem do GitHub z pełną zawartością pliku konfiguracyjnego.
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="pl.juniorjavadeveloper.examples.hibernate.basic.configuration.pu">
<!-- https://github.com/juniorjavadeveloper-pl/hibernate-examples/blob/master/src/test/resources/META-INF/persistence.xml -->
</persistence-unit>
</persistence>
Spring Data JPA – JpaRepository / CrudRepository
Korzystając z Spring Framework mamy możliwość dodania modułu Spring Data JPA, aby uprościć dostęp do danych w aplikacjach Spring korzystających z JPA.
Repository
Javadoc: Central repository marker interface. Captures the domain type to manage as well as the domain type’s id type. General purpose is to hold type information as well as being able to discover interfaces that extend this one during classpath scanning for easy Spring bean creation.
Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the same signature as those declared in CrudRepository.
@Repository
public interface AnimalRepository extends JpaRepository<Animal, Long> {
}
Oficjalny tutorial -> Accessing Data with JPA
Spring Boot dostarcza nam domyślną konfiguracje w postaci pliku properties – application.properties. Poniżej przykład konfiguracji.
spring.datasource.url=jdbc:h2:~/hibernate-examples spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=update
Gdzie używać Hibernate
Poniżej w telegraficznym skrócie opiszę, gdzie z mojego doświadczenia można użyć różne sposoby korzystania z Hibernate.
Native – aplikacje stand-alone
Jeżeli chcemy “lekką” aplikację i nie potrzebujemy całego “bagażu” związanego z Java EE oraz Spring Framework, to w zupełności wystarczy nam Hibernate Native API. Używałem tej opcji do tworzenia aplikacji desktopowych np.: w JavaFX lub dla oddzielnych, niezależnych modułów aplikacji.
JPA-standard – aplikacje Java EE
Tworząc aplikacje Java EE możemy użyć JPA, ale wtedy musimy dodać (plik JAR) implementację JPA, może, to być właśnie Hibernate. Aplikacje dla Java EE umieszczamy na serwerach aplikacyjnych, które mogą mieć dostępną implementację JPA, wtedy, po prostu podmieniamy ją na Hibernate.
Spring Data JPA – aplikacje dla Spring Framework
Odpowiedź na to pytanie jest oczywista – aplikacje dla Spring Framework.
Czy ktoś w ogóle używa Hibernate?
Uwaga, na koniec granat! Z mojego komercyjnego doświadczenia wynika, że Hibernate nie jest używany w systemach produkcyjnych. Jak, to możliwe?! Niestety Hibernate jest wolny i niewydajny, ma za duży narzut generycznych, ogólnych koncepcji i rozwiązań. Jest, to “kombajn” do wszystkiego i dla każdej bazy danych. Hibernate bardzo dobrze się sprawdza do szybkiego prototypownia oraz tworzenia aplikacji typu proof-of-concept. No i oczywiście do tworzenia aplikacji CRUD do naszego portfolio na potrzeby rekrutacji 😉 Jeżeli nie Hibernate, to co? Oczywiście dobre stare JDBC i/lub Spring JdbcTemplate.
Lesson: JDBC Basics (The Java™ Tutorials > JDBC Database Access)
In this lesson you will learn the basics of the JDBC API. Źródło: https://docs.oracle.com/javase/tutorial/jdbc/basics/index.html
Accessing Relational Data using JDBC with Spring
You will build an application that uses Spring’sJdbcTemplateto access data stored in a relational database. Źródło: https://spring.io/guides/gs/relational-data-access/
Podsumowanie
Wierzę, że rzucony na koniec granat nie zniechęci do korzystania z Hibernate. Kiedy już poznamy możliwości Hibernate, to łatwiej będzie zastosować do naszych potrzeb konkretny sposób korzystania z Hibernate. Mam nadzieję, że osoby uczące się Hibernate szukając rozwiązań w internecie już nie utoną w morzu możliwości jakie daje Hibernate. Korzystając z Hibernate łatwo i szybko tworzy się aplikacje typu proof-of-concept.