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
SessionFactory factory = new Configuration () . configure () . buildSessionFactory () ;
Session session = factory . openSession () ;
session . beginTransaction () ;
session . persist ( new User ( " Andrzej " )) ;
session . getTransaction () . commit () ;
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.