JPA #6 - Create

JPA #6 – Create

Zaczynamy działać w obszarze CRUD i dziś zajmiemy się pierwszą operacją z tej rodziny czyli create. Poznasz przy tej okazji zasadę działania transakcji.

CRUD to określenie na cztery podstawowe działania w aplikacji korzystającej z pamięci trwałej czyli bazy danych: create, read, update, delete.

Czas na stworzenie pierwszego obiektu i zapisaniu go do bazy danych, a więc zapoznamy się z operacją Create.

Refaktoring naszego kodu

Zanim przejdziemy dalej warto by uporządkować nieco obecny kod, a właściwie jego rozmieszczenie, aby trzymać się dobrych praktyk.

Refaktoring kodu to proces wprowadzania zmian w programie, który nie ma na celu zmiany funkcjonalności, a jedynie utrzymanie wysokiej jakości organizacji systemu. 

Repozytoria są to specyficzne typy klas, które służą za kontakt z bazą danych.

Utwórz nową klasę o nazwie GuestRepository i przenieś z klasy App naszego Entity Managera. Wciąż zostawmy go statycznego, jednak w ten sposób będzie bardziej przejrzyście.

Po tym małym sprzątaniu kod w nowo utworzonej klasie powinien wyglądać w ten sposób:

package pl.clockworkjava.hotelreservation.jpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class GuestRepository {

    private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("thePersistenceUnit");
    private static EntityManager em  = factory.createEntityManager();
}

Wracając do klasy App. W metodzie main tworzymy nowy obiekt GuestRepository.

package pl.clockworkjava.hotelreservation.jpa;

public class App {

    public static void main(String[] args) {
        GuestRepository repo = new GuestRepository();
        System.out.println("Hello");
    }
}

Repozytoria w naszym kodzie odpowiadają za tworzenie nowych obiektów i zapisywanie ich do bazy danych.

W związku z tym używając repozytorium GuestRepository o nazwie repo i stwórz nowego gościa o danych (String name) Paweł i wieku (int age) 34 lata. Utwórz od razu tą metodę w GuestRepository.

package pl.clockworkjava.hotelreservation.jpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class GuestRepository {

    private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("thePersistenceUnit");
    private static EntityManager em  = factory.createEntityManager();

    public void createNewGuest(String name, int age) {
        
    }
}
package pl.clockworkjava.hotelreservation.jpa;

public class App {

    public static void main(String[] args) {
        GuestRepository repo = new GuestRepository();
        repo.createNewGuest("Paweł", 34);
    }
}

Tworzenie obiektu

Aby zapisać obiekt do bazy danych najpierw należy go utworzyć. Na tą chwilę pomijamy fakt, że obiekt jest encją. Dla nas jest to teraz tylko obiekt Javowy.

package pl.clockworkjava.hotelreservation.jpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class GuestRepository {

    private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("thePersistenceUnit");
    private static EntityManager em  = factory.createEntityManager();

    public void createNewGuest(String name, int age) {

        Guest newOne = new Guest(name, age);
    }
}

Natomiast, żeby zapisać go do bazy danych musimy użyć Entity Managera, bo to on kontroluje zapisywanie do niej danych. Do tego użyjemy metody persist, do której prześlemy nasz obiekt. To jednak nie wszystko. Do tej pory jedynie jesteśmy na etapie, że Entity Manager wysyła sygnał do Persistence Context, żeby nasz obiekt został w nim zapisany.

package pl.clockworkjava.hotelreservation.jpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class GuestRepository {

    private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("thePersistenceUnit");
    private static EntityManager em  = factory.createEntityManager();

    public void createNewGuest(String name, int age) {

        Guest newOne = new Guest(name, age);

        em.persist(newOne);
    }
}

O ile taki obiekt nie psuje aplikacji to samo create jest niewystarczające. Gdzie nasz insert z bazy danych? Musimy się bardziej postarać.

Na razie niewiele nam dało stworzenie samego obiektu i zawołanie persist – reakcja programu po odpaleniu

Do tej pory 1) wywołano nasz komentarz “— CREATE—“, następnie po utworzeniu nowego obiektu newOne i wykonaniu em.persist(newOne) 2) pobraliśmy nową sekwencję hibernate_sequence z puli ID i zostało one przypisane dla naszego obiektu newOne.

Zatem dodajmy jeszcze coś do kodu.

Wypisz proszę po tym wszystkim komunikat “NewGuest ID” i pokaż nr ID na konsoli. Metodę get.ID utwórz w Guest.java tak jak podpowiada.

package pl.clockworkjava.hotelreservation.jpa;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

public class Guest {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;
    private int age;

    public Guest(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public long getId() {
        return this.id;
    }
}
package pl.clockworkjava.hotelreservation.jpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class GuestRepository {

    private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("thePersistenceUnit");
    private static EntityManager em  = factory.createEntityManager();

    public void createNewGuest(String name, int age) {

        System.out.println(" ------------ CREATE ------------ ");

        Guest newOne = new Guest(name, age);

        em.persist(newOne);

        System.out.println(" New Guest ID " + newOne.getId());
    }
}

Dopiero teraz mimo braku zapisu obiektu do bazy danych otrzymuje on jakieś ID.

Zapis danych do bazy danych

Prześledźmy teraz w jaki sposób działa tworzenie i zapisywanie danych do bazy danych na schemacie poniżej. Nasz obiekt newOne (Paweł, 32) widzimy po lewej stronie. Na Entity Manager wywołaliśmy metodę persist z encją/naszym obiektem Javowym newOne i dzięki temu zostaje “wrzucona” do niego. Pamiętamy – zarządza, zapisuje zmiany, śledzi co się dzieje z encjami. Zdecydowaliśmy, że ID będzie generowane automatycznie, a już na poziomie EM będziemy go potrzebować. W związku z tym wysyłamy prośbę żeby baza danych nadała odpowiedni ID – w naszym wypadku 1. Nasz obecny kod zatrzymuje się w tej chwili.

Przejdźmy z kodem do momentu, kiedy to wszystko co zebraliśmy zostanie faktycznie zapisane w bazie danych co obrazuje poniższy schemat.

Entity Manager zarządza Persistence Context i to tak na prawdę w tym “żyją” nasze encje. Dodatkowo wszelkie czynności, które modyfikują dane w bazie danych (zapis, aktualizacja, usuwanie) muszą być opakowane w transakcję.

Transakcja określa nam taki kawałek kodu, który musi się wykonać w całości. Dopiero po zakończeniu wszystkich procesów są one zapisywane w bazie danych.

Rozpoczęcie transakcji zapoczątkuje czas, od którego będzie śledzone czy wszystkie operacje wykonywane na encjach się powiodły. Sygnałem na zakończenie tego będzie ustalenie kiedy transakcja się kończy. Wtedy nastąpi zapis encji do bazy danych.

Przejdźmy do konkretów w kodzie.

Za pomocą Entity Manager pobieramy metodę getTransaction() i zapisujemy ją do lokalnej zmiennej EntityTransaction, którą nazwiemy transaction. Teraz możemy zapoczątkować proces transakcji za pomocą metody begin(). Po wypisaniu ID nowego gościa zamykamy transakcję metodą commit(). Można też użyć rollBack(), które cofnie zmiany i zamknie transakcję. W celu jasnego uwidocznienia naszych ruchów obie akcje – start i zakończenie – będzie opisane i pojawi się na ekranie konsoli. Zatem nasz kod wygląda tak:

package pl.clockworkjava.hotelreservation.jpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class GuestRepository {

    private static EntityManagerFactory factory = Persistence.createEntityManagerFactory("thePersistenceUnit");
    private static EntityManager em  = factory.createEntityManager();

    public void createNewGuest(String name, int age) {

        System.out.println(" ------------ CREATE ------------ ");

        Guest newOne = new Guest(name, age);
        EntityTransaction transaction = em.getTransaction();

        transaction.begin();

        System.out.println(" ------ Persisting in new transaction ------ ");

        em.persist(newOne);

        System.out.println(" New Guest ID " + newOne.getId());
        System.out.println(" ------ Closing transaction ------ ");

        transaction.commit();
    }
}
Wypisanie na konsoli czynności od początku do końca transakcji i zapis danych do bazy danych

Najpierw tworzymy nasz nowy obiekt. Później otwieramy transakcję. Widzimy zawołanie o nadanie ID i wypisany jego numer (jest to niezależne od transakcji, ale potrzebujemy go do naszych celów). Następnie kończymy transakcje metodą commit() co daje nam zapisanie danych do bazy danych w ostatniej linijce za pomocą języka SQL.

Podsumowanie

Sama metoda persist() nie wystarczy, żeby zapisać dane do bazy danych.

Użyj transakcji. Wtedy proces zapisywany jest dopiero po sukcesie wszystkich czynności wywoływanych w trakcie jej trwania.

Kod aplikacji znajdziesz w publicznym repozytorium GitHub.

By być na bieżąco i mieć realny wpływ na tematykę tworzonych artykułów zapraszam do dołączenia do mojego newslettera.


Informacje oparte zostały o materiał darmowego kursu wprowadzającego w świat JPA i Hibernate “Fundamenty JPA i Hibernate” dostępnego w formie wideo na platformie ClockworkJava.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *