Skip to content

Wykład 4 - ORM, JPA i Hibernate

  • Dużo kodu (ręczne połączenia, PreparedStatement/ResultSet, zamykanie zasobów).
  • Ręczne mapowanie ResultSet → obiekty Java (boilerplate, podatność na błędy).
  • Trudne utrzymanie: zmiana schematu = zmiany w wielu miejscach; mieszanie logiki z SQL.
  • Brak abstrakcji: silne powiązanie z konkretną bazą, słaba przenośność.
  • Pełna kontrola nad SQL i precyzyjna optymalizacja; brak narzutu warstwy abstrakcji (lepsza wydajność, brak problemu N+1).
  • Przejrzystość (wiadomo, jakie zapytanie trafia do bazy); lepsze dla złożonych JOIN-ów/raportów; niezależność od frameworków.
  • Technika mapowania obiektów Java na struktury relacyjnej bazy: encja ↔ tabela, pole ↔ kolumna.
  • Automatyzacja CRUD przez metody (persist, merge, remove), generowanie SQL przez framework.
  • Zalety: mniej kodu SQL/boilerplate, przenośność między bazami, łatwiejsze utrzymanie, oddzielenie logiki od warstwy danych.
  • Persystencja = trwałość danych.
  • JPA to specyfikacja (standard Java EE / Jakarta EE) opisująca pracę z relacyjnymi bazami - zestaw interfejsów i adnotacji (@Entity, @Id).
  • Główne komponenty: EntityManager, EntityManagerFactory.
  • Implementacje JPA: Hibernate, EclipseLink. JPA standaryzuje ORM → można zmienić implementację bez zmiany kodu.
  • Popularny framework ORM, implementacja specyfikacji JPA.
  • JPA = standard (interfejs/kontrakt), Hibernate = implementacja (konkretna realizacja).
  • Rozszerzenia ponad JPA: API Session, Criteria, 2nd level cache, więcej opcji konfiguracji.
Poziom Konstrukcja Charakterystyka
Low-level Session (Hibernate) pełna kontrola, HQL, Criteria, API specyficzne dla Hibernate
Standard EntityManager (JPA) abstrakcja nad ORM, przenośność, JPQL, Criteria
High-level JpaRepository (Spring Boot) automatyczne CRUD, query derivation, Spring Data JPA

Relacje: Hibernate implementuje JPA → EntityManager może działać na Session; JpaRepository korzysta wewnętrznie z EntityManager.

  • Session (Hibernate): plik hibernate.cfg.xml + SessionFactory.
  • EntityManager (JPA): plik META-INF/persistence.xml + EntityManagerFactory.
  • Zależności Maven: hibernate-core, h2, jakarta.persistence-api.
Wartość Działanie
none brak działań na schemacie (częsty domyślny)
validate sprawdza zgodność schematu z encjami, bez zmian
update aktualizuje schemat (dodaje kolumny/tabele), zachowuje dane
create tworzy schemat za każdym startem, usuwa poprzednie dane
create-drop tworzy przy starcie, usuwa przy zamknięciu (do testów)
create-only tworzy schemat i kończy, bez aktualizacji
// Session (Hibernate)
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
session.beginTransaction();
session.persist(new User("Andrzej"));
session.getTransaction().commit();
// EntityManager (JPA)
EntityManager em = Persistence.createEntityManagerFactory("UserPU").createEntityManager();
em.getTransaction().begin();
em.persist(new User("Andrzej"));
em.getTransaction().commit();
  • Klasa Java odwzorowująca tabelę; obiekt = jeden rekord (wiersz).
  • Wymagania: adnotacja @Entity, klucz główny @Id, konstruktor bezargumentowy (wymagany przez Hibernate).
Adnotacja Znaczenie
@Entity klasa zarządzana przez JPA
@Id klucz główny (wymagany)
@GeneratedValue auto-generowanie klucza; strategie: IDENTITY, SEQUENCE, TABLE
@Table(name="...") nazwa tabeli (opcjonalne, domyślnie nazwa klasy)
@Column mapowanie pola na kolumnę (name, nullable, length, unique)
Stan Opis
transient nowy obiekt, nie zapisany w bazie (tylko w pamięci)
managed zarządzany przez persistence context, zmiany synchronizowane z DB (dirty checking)
detached odłączony od kontekstu, zmiany nie są śledzone
removed oznaczony do usunięcia

Sprawdzenie zarządzania: em.contains(entity)true = managed.

Adnotacja Relacja Przykład
@OneToMany jeden → wiele Customer → Orders
@ManyToOne wiele → jeden Order → Customer
@OneToOne 1:1 User → Profile
@ManyToMany wiele do wielu (tabela pośrednia) Student ↔ Courses
  • @JoinColumn(name="...") - klucz obcy w tabeli.
  • mappedBy - wskazuje właściciela relacji (strona bez własnej tabeli pośredniej / strona odwrotna).
  • @JoinTable(name, joinColumns, inverseJoinColumns) - tabela pośrednia dla @ManyToMany.
  • PERSIST - zapis encji nadrzędnej zapisuje powiązane (Customer → Orders).
  • MERGE - aktualizacja obejmuje powiązane (przy detached).
  • REMOVE - usunięcie encji usuwa powiązane (uwaga: niezamierzone usunięcia!).
  • ALL - wszystkie operacje; REFRESH, DETACH - rzadsze.
Podejście Charakterystyka Przykład
API ORM praca na obiektach, bez zapytań em.persist(), em.find(), em.merge(), em.remove()
JPQL / HQL zapytania na encjach (nie tabelach), niezależne od bazy em.createQuery("SELECT u FROM User u", User.class)
Criteria API dynamiczne, typowane zapytania budowane programistycznie cb.createQuery(User.class)
Native SQL bezpośredni SQL, pełna kontrola, zależność od bazy em.createNativeQuery("SELECT * FROM users")
First-level (L1) Second-level (L2)
Zakres Session / EntityManager współdzielony między sesjami
Aktywacja automatyczny, zawsze włączony wymaga konfiguracji (np. Ehcache)
Czas życia lokalny, krótkotrwały globalny, trwały
Użycie zawsze kluczowy aplikacje produkcyjne

Ponowne pobranie tej samej encji w sesji nie generuje zapytania SQL (L1). Zła konfiguracja L2 → niespójne dane.

Do zapamiętania: JPA=standard, Hibernate=implementacja; encja wymaga @Entity + @Id + konstruktor bezargumentowy; 4 stany cyklu życia; mappedBy wskazuje właściciela; hbm2ddl: update zachowuje dane, create kasuje; L1 zawsze włączony.