Przyszedł czas na połączenie całej zdobytej wiedzy i stworzenie pierwszego serwisu opartego na CRUD. Pisząc serwis nie mam namyśli serwisu www, chodzi o klasę, która ma specjalne przeznaczenie i zawiera operacje CRUD – Create, Read, Update, Delete.

Bardzo wiele aplikacji jest tzw. CRUD’ami, czyli udostępnia podstawowe operacje tworzenia (create), odczytu (read), modyfikacji (update) oraz usuwania (delete) obiektów jakiejś klasy. Dla przykładu weźmy katalog samochodów, gdzie mamy klasę Car.

Poniżej operacje CRUD dla klasy Car:
– CREATE – dodawanie samochodu do katalogu,
– READ – odczytanie szczegółów samochodu z katalogu,
– UPDATE – zmiana, modyfikacja informacji o samochodzie w katalogu,
– DELETE – usunięcie samochodu z katalogu.

Dodatkowo z CRUD’em związana jest metoda list(), zwracająca listę wszystkich obiektów, na których operuje CRUD, czyli w naszym przypadku metoda list() zwróci listę wszystkich samochodów w katalogu.

W poprzednich artykułach opisałem teorię i pokazałem przykłady kodu dotyczące tworzenia pierwszej klasy w języku Java, jak i gdzie zapisać wartości w klasie, jak wykonywać działania na klasie, jak ukryć kod klasy przed niepowołanym dostępem oraz przedstawiłem tablice i listy w języku Java. Te pięć artykułów jest niezbędne do stworzenia serwisu z operacjami CRUD.

Zanim zacznę pisać dlaczego w ogóle stosować CRUD i dlaczego jest on tak ważny poniżej przedstawię klasę Java, która jest CRUD’em, posłuży nam jako szablon dla innych klas będących CRUD’ami. Oczywiście poniższa klasa nic nie robi, zawiera tylko metody, które nie przyjmują żadnych parametrów, ani nie zwracają żadnej wartości.

// CRUD - template class
public class CrudServiceTemplate {
    // C - create
    public void create() {
    }
    // R - read
    public void read() {
    }
    // U - update
    public void update() {
    }
    // D - delete
    public void delete() {
    }
    // L - list
    public void list() {
    }
}

CRUD jest nierozerwalnie związany z klasą, dla której wykonywane są operacje, żeby mieć poprawny i funkcjonalny CRUD potrzebujemy jeszcze jednej klasy. Klasy, na których operuje CRUD są po prostu zwykłymi POJO, czyli klasami, które mają atrybuty/zmienne/pola do tego konstruktory i metody get/set. Takie klasy często są określane mianem: model, encja, DTO, VO.

Poniżej przykład klasy, z której będzie korzystał CRUD.

public class Car {
    private int year;
    private String model;
    private String manufacturer;
    private BigDecimal price;

    // constructors
    // getters/setters
    // toString()
}

Mając powyższą klasę Car oraz szablon dla klas z CRUD (CrudServiceTemplate) stworzymy klasę będącą serwisem, czyli zawierającą logikę odnośnie CRUD’a dla klasy Car. Należy zauważyć, że nie ma już potrzeby dodawania w nazwie klasy słowa Crud. Poniżej wspomniana klasa, na razie zwraca ona wartości null dla czytelności kodu.

// CRUD Service dla klasy Car
public class CarService {

    // lista przechowująca wszystkie samochody
    // tylko i wyłącznie dla uproszczenia kodu aplikacji
    // docelowo dane powinny być przechowywane w bazie danych
    private List<Car> carsCatalog = new ArrayList<>();

    // C - create
    public Car create(Car car) {
        return null;
    }
    // R - read
    public Car read(Integer id) {
        return null;
    }
    // U - update
    public Car update(Integer id, Car car) {
        return null;
    }
    // D - delete
    public void delete(Integer id) {
    }
    // L - list
    public List<Car> list() {
        return null;
    }
}

CRUD jest ważny ponieważ systematyzuje i grupuje w jednym miejscu operacje, które można wykonać na klasie będącej, np. modelem. CRUD można zastosować dla wielu klas w tym samym projekcie. Dzięki zastosowaniu CRUD w kontekście serwisu, klasy Service separujemy dane zawarte w np. modelu od logiki aplikacji, która używa tego modelu.

Poniżej opiszę oddzielnie każdą metodę z CRUD dla klasy CarService, a na koniec przedstawię całą klasę z pełną implementacją logiki biznesowej wraz z użyciem klasy CarService.

Należy zauważyć, że CarService jest uproszczoną wersją, która zakłada przechowywanie wyników operacji metod CRUD na liście obiektów private List<Car> carsCatalog; – katalogu samochodów – docelowo dane powinny być przechowywane w bazie danych. Na potrzeby tego przykładu private List<Car> carsCatalog; będzie symulować bazę danych.

Symulując bazę danych za pomocą private List<Car> carsCatalog; musimy pamiętać o tym, że w bazie danych każdy wiersz powinien mieć unikalny identyfikator, osiągniemy, to za pomocą private Random randomCarId;.

Dlaczego operujemy na unikalnych identyfikatorach? Ponieważ może być wiele takich samych samochodów, tzn. ich marka, model, rok produkcji będą identyczne. Dlatego musimy wprowadzić unikalny identyfikator, taki identyfikator jest standardową praktyką.

Poniżej przykład listy samochodów – katalogu samochodów – z unikalnymi identyfikatorami.

idyearmodelmanufacturerprice
12016Model XTesla340 000 zł
42009CorollaToyota33 000 zł
992020XC 90Volvo300 000 zł

Poniżej szczegółowy opis każdej z metod z CRUD. Należy zauważyć, że nad każdą metodą jest specjalny komentarz w postaci /** Treść komentarza */ jest, to Javadoc, specjalna forma komentarza, która jest rozpoznawana przez IDE takie jak IntelliJ i wyświetlane w postaci Tooltip oraz służy do tworzenia dokumentacji do projektu. Dla przykładu dokumentacja klasy Object jest wygenerowana z Javadoc.

Javadoc Tooltip - Pierwsza klasa jako serwis CRUD – kod Java, IntelliJ, krok po kroku
Javadoc Tooltip – Pierwsza klasa jako serwis CRUD – kod Java, IntelliJ, krok po kroku

C – Create – tworzenie samochodu za pomocą serwisu.

/**
 * C - create - tworzy/zapisuje samochód do katalogu
 * @param car Dane samochodu do utworzenia/zapisania w katalogu
 * @return Utworzony samochód z unikalnym identyfikatorem
 */
public Car create(Car car) {
    // generowanie unikalnego identyfikatora samochodu
    int id = randomCarId.nextInt(100_000);
    // ustawienie unikalnego identyfikatora dla samochodu
    car.setId(id);

    // dodanie nowego samochodu do katalogu
    carsCatalog.add(car);
    // zwrócenie utworzonego samochodu z unikalnym identyfikatorem
    return car;
}

R – Read – odczytywanie samochodu za pomocą serwisu.

/**
 * R - read - odczytuje/wyszukuje samochód z katalogu
 * @param id Unikalny identyfikator samochodu w katalogu
 * @return Odczytany z katalogu samochód
 */
public Car read(Integer id) {
    // iterujemy po liście/katlogu samachodów
    for (Car car : carsCatalog) {
        // dla każdego samochodu w katalogu ...
        // ... sprawdzamy czy identyfiktory zgadzają się
        if (car.getId().equals(id)) {
            // jeżeli identyfikatory są zgodne, to ...
            // ... zwracamy wyszukany/odczytany samochód
            return car;
        }
    }
    // jeżeli nie znaleziono szukanego samochodu zwracamy null
    return null;
}

U – Update – modyfikacja danych samochodu za pomocą serwisu.

/**
 * U - update - modyfikuje dane samochodu w katalogu.
 * @param id Identyfikator samochodu do modyfikacji danych
 * @param carUpdate Aktualne/nowe dane samochodu
 * @return Zmodyfikowany samochód w katalogu
 */
public Car update(Integer id, Car carUpdate) {
    // iterujemy po liście/katlogu samachodów
    for (Car car : carsCatalog) {
        // dla każdego samochodu w katalogu ...
        // ... sprawdzamy czy identyfiktory zgadzają się
        if (car.getId().equals(id)) {
            // jeżeli identyfikatory są zgodne, to ...
            // ... modyfikujemy dane samochodu ...
            car.setYear(carUpdate.getYear());
            car.setModel(carUpdate.getModel());
            car.setManufacturer(carUpdate.getManufacturer());
            car.setPrice(carUpdate.getPrice());
            
            // ... zwracamy zmodyfikowany samochód 
            return car;
        }
    }
    // jeżeli nie znaleziono szukanego samochodu zwracamy null
    return null;
}

D – Delete – usuwanie samochodu za pomocą serwisu.

/**
 * D - delete - usuwa samochód z katalogu.
 * @param id Identyfikator samochodu do usunięcia z katalogu.
 */
public void delete(Integer id) {
    // tworzymy Iterator dla naszej listy,
    // aby uniknąć wyjątku ConcurrentModificationException 
    // podczas jednoczesnego iterowania po liście
    // i usuwania z niej elementów
    Iterator<Car> iterator = carsCatalog.iterator();
    // wykonujemy operacje dopóki Iterator ma kolejne elementy
    while (iterator.hasNext()) {
        // pobieramy kolejny element
        Car car = iterator.next();
        // jeżeli identyfikatory są zgodne, to ...
        if (car.getId().equals(id)) {
            // ... usuwamy samochód z listy
            iterator.remove();
        }
    }
}

Przyszedł czas na użycie powyższych metod, tak jak widać powstało bardzo dużo kodu, który jeszcze nie został użyty i nie wiadomo czy spełnia nasze oczekiwania i czy w ogóle działa. Oczywiście w prawdziwym projekcie powstałyby najpierw testy jednostkowe zgodnie z TDD. Na potrzeby przykładów dla osób dopiero zaczynających przygodę z językiem Java powyższy kod jest wystarczający.

    public static void main(String[] args) {
        System.out.println("Katalog samochodów z wykorzystaniem CarService - CRUD'a");

        // stworzenie obiektu będącego CRUD'em
        CarService carService = new CarService();

        System.out.println("Zawartość katalogu samochodów przed dodanie zawartości");
        List<Car> carsCatalog = carService.list();
        System.out.println(carsCatalog + "\n");

        System.out.println("Dodanie jednego samochodu do katalogu");
        Car toyotaCorollaCar = new Car(2009, "Corolla", "Toyota", BigDecimal.valueOf(33_000.00));
        System.out.println("Samochód poza katalogie nie posiada unikalnego identyfikatora");
        System.out.println(toyotaCorollaCar);
        Car createdToyotaCorollaCar = carService.create(toyotaCorollaCar);
        System.out.println("Utworzony/dodany samochód w katalogu posiada unikalny identyfikator");
        System.out.println(createdToyotaCorollaCar + "\n");

        System.out.println("Zawartość katalogu samochodów po dodaniu jednego samochodu");
        System.out.println(carService.list() + "\n");

        System.out.println("Dodanie kolejnego samochodu do katalogu");
        Car teslaModelXCar = new Car(2016, "Model X", "Tesla", BigDecimal.valueOf(340_000.00));
        carService.create(teslaModelXCar);

        System.out.println("Zawartość katalogu samochodów po dodaniu kolejnego samochodu");
        System.out.println(carService.list() + "\n");

        System.out.println("Modyfikacja danych samochodu z katalogu");
        // ... najpierw pobieramy katalog samochodów, aby znać identyfikatory samochodów ...
        List<Car> carsCatalogUpdate = carService.list();
        // ... wybieramy pierwszy samochód do modyfikacji danych ...
        Integer firstCarId = carsCatalogUpdate.get(0).getId();
        Car firstCar = carService.read(firstCarId);
        System.out.println("Dane samochodu do modyfikacji: " + firstCar);
        // ... podajemy nowe dane samochodu ...
        Car firstCarUpdate = new Car();
        firstCarUpdate.setId(firstCar.getId());
        firstCarUpdate.setYear(firstCar.getYear());
        firstCarUpdate.setModel("Corolla Sport");
        firstCarUpdate.setManufacturer(firstCar.getManufacturer());
        firstCarUpdate.setPrice(BigDecimal.valueOf(35_000.00));
        System.out.println("Dane samochodu po modyfikacji: " + firstCarUpdate);
        System.out.println("W katlogu jeszcze są stare dane");
        System.out.println(carsCatalog + "\n");
        // ... identyfikator samochodu jest przekazywany do metody update() ...
        Integer firstCarToUpdateId = firstCar.getId();
        // ... aktulizujemy dane samochodu w katalogu za pomocą serwisu ...
        Car update = carService.update(firstCarToUpdateId, firstCarUpdate);

        System.out.println("W katlogu są już nowe dane");
        System.out.println(carsCatalog + "\n");

        // ... usuwamy jedne samochód z katalogu ...
        // ... najpierw pobieramy katalog samochodów, aby znać identyfikatory samochodów ...
        List<Car> carsCatalogDelete = carService.list();
        // .. usuwamy samochód za pomocą serwisu ...
        Car firstCarToDelete = carsCatalogDelete.get(0);
        Integer firstCarToDeleteId = firstCarToDelete.getId();
        carService.delete(firstCarToDeleteId);

        System.out.println("Samochód został usunięty z katalogu");
        System.out.println(carsCatalog + "\n");
    }
}
Wynik działania klasy CarServiceMain - Pierwsza klasa jako serwis CRUD – kod Java, IntelliJ, krok po kroku
Wynik dziaÅ‚ania klasy CarServiceMain – Pierwsza klasa jako serwis CRUD – kod Java, IntelliJ, krok po kroku

Całość kodu można znaleźć na:

Zapraszam do regularnego odwiedzania mojej strony, będą pojawiać się kolejne artykuły oraz do kontaktu przez email kontakt(at)juniorjavadeveloper.pl.