JavaRush /Java блог /Random UA /Spring – це не страшно, або як наповнити h2 базу даних (і...
Павел
11 рівень

Spring – це не страшно, або як наповнити h2 базу даних (і трохи Hibernate)

Стаття з групи Random UA
ЗМІСТ ЦИКЛУ СТАТТІВ Продовжуємо наш проект. Як створити веб-проект. Підключаємо базу до свого веб-проекту . Цього разу літер буде більше, п'ять хвабон не обійдемося. У минулій статті я радив почитати про Spring кілька сторінок або кілька статей , або хоча б погуглити про що таке біни, контекст, сутності, впровадження залежностей у spring, способи конфігурації бінів. Якщо ні, то раджу це зробити одразу зараз чи після цієї статті. Перед тим як заповнювати нашу h2 базу. Необхідно створити клас утиліту для запуску в ній методів заповнення бази. У пакеті
ru.java.rush
Створюємо пакет для утиліт
utils
І саму утиліту:
package ru.java.rush.utils;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;

@Service //анотація помечает бин як сервис
public class InitiateUtils implements CommandLineRunner { //имплементируем интерфейс CommandLineRunner (командная строка запуска)

    @Override
//переопределяем метод который позволит
//нам выполнять методы нашего програми при запуске
    public void run(String... args) throws Exception {
        System.out.println("run"); //проверим что это работает
    }
}
Запустимо програму і в консоль виведеться "run" . Нам потрібна ця утиліта, як альтернатива класу Applications, оскільки він повинен відповідати тільки за запуск програми. Створимо сутності. Сутність – це бін, мета якого зберігати деякі дані. У пакеті
ru.java.rush
Створюємо пакет для сутностей
entities
І саму сутність, нехай це буде фрукти:
package ru.java.rush.entities;

import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;

@Entity//помечаем бин як сущность
@Table(name = "fruit_table")//в этой аннотации можно указать ім'я создаваемой таблицы
public class FruitEntity {

    @Id//анотація из пакета avax.persistence.*, помечает поле як id
    @Column(name = "id_fruit")//в этой аннотации можно указать ім'я поля
    @GenericGenerator(name = "generator", strategy = "increment")//незаметно добрались до hibernate,
// здесь указывается что id будет автоматически увеличиваться при новых записях
    @GeneratedValue(generator = "generator")//анотація генерации id
    private Integer id;

    @Column(name = "fruit_name")
    private String fruitName;

    @Column(name = "provider_code")
    private Integer providerCode;

   //что бы в с классом можно было совершать манипуляции создается
  //пустой конструктор, геттеры, сеттеры и переопределяется метод toString()

  public FruitEntity(){ //пустой конструктор

 }

public Integer getId() {
    return id;
}

 //геттеры, сеттеры
public String getFruitName() {
    return fruitName;
}

public FruitEntity setFruitName(String fruitName) {
    this.fruitName = fruitName;
    return this;
}

public Integer getProviderCode() {
    return providerCode;
}

public FruitEntity setProviderCode(Integer providerCode) {
    this.providerCode = providerCode;
    return this;
}

//переопределяем toString()
@Override
public String toString() {
    return "FruitEntity{" +
            "id=" + id +
            ", fruitName='" + fruitName + '\'' +
            ", providerCode=" + providerCode +
            '}';
}
}
Конструктор, геттери, сеттери і toString() не обов'язково писати руками, можна швидко нагенерувати . Гаразд, сутність у нас взаємодіє з базою і зберігає дані з БД. Сутність при справі. Але хтось має оперувати сутністю в додатку. Для цього вигадали "репозиторій". У пакеті
ru.java.rush
Створюємо пакет для репозиторіїв
repositories
І сам репозиторій
package ru.java.rush.repositories;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import ru.java.rush.entities.FruitEntity;

@Repository//помечаем что этот бин - репозиторий
public interface FruitRepository extends JpaRepository<FruitEntity,Integer> {
//репозиторий является интерфейсом, который наследуется от другого интерфейса JpaRepository<>
//для него необходимо указать с якой сущность он должен работать, у нас это FruitEntity
//и тип данных у поля id данной сущности, у нас это Integer
}
Питання, чому тіло інтерфейсу порожнє, немає жодного методу, що він оголошує? Для відповіді затиснемо Ctrl і клікнемо на JpaRepository і подивимося що він і сам успадковується від PagingAndSortingRepository<t, ""> і QueryByExampleExecutor<>, вони теж оголошують якісь методи. Не буду сюди копіювати методи самі по дивіться. Оскільки репозиторій це інтерфейс, він ні чого не робить, він тільки оголошує методи, необхідний ще хтось, щоб ці методи реалізувати. Для цього придумано "сервіс". У пакеті
ru.java.rush
Створюємо пакет для сервісів
services
І сам сервіс
package ru.java.rush.services;

import org.springframework.stereotype.Service;

@Service//помечаем что этот бин - сервис
public class FruitService {
}
Зараз ми дійшли до важливого моменту: "Як і навіщо заінжектити бін" (впровадити залежність). Якщо ви не розумієте про що це я, то прошу вас почитати на цю тему, зараз чи після, особливо зверніть увагу на способи "інжекта", скільки їх, який краще, який гірший, і чому. Ми використовуємо один із способів. Нам потрібно щоб "сервіс" як то був пов'язаний з "репозиторієм". Доповнюємо наш сервіс анотацією та змінною.
package ru.java.rush.services;

import org.springframework.stereotype.Service;
import ru.java.rush.repositories.FruitRepository;

@Service
public class FruitService {

    private final FruitRepository fruitRepository;  //final переменная репозитория

public FruitService(FruitRepository fruitRepository) {//внедрабо зависимость через конструктор
    this.fruitRepository = fruitRepository;
}

}
Тепер можна реалізувати метод із "репозиторію" Доповнюємо "сервіс"
package ru.java.rush.services;

import org.springframework.stereotype.Service;
import ru.java.rush.entities.FruitEntity;
import ru.java.rush.repositories.FruitRepository;

@Service
public class FruitService {

    private final FruitRepository fruitRepository;

public FruitService(FruitRepository fruitRepository) {//внедабо зависимость
    this.fruitRepository = fruitRepository;
}

//создали публичный метод (название любое может быть)
//на вход принимает сущность и сохраняет ее в базу
    public void save(FruitEntity fruitEntity){
        fruitRepository.save(fruitEntity); //реализовали метод внедренного бина
    }

//возвращает лист всех сущностей из базы
    public List<FruitEntity> getAll(){
       return fruitRepository.findAll(); //реализовали метод внедренного бина
    }
}
Залишилося тільки реалізувати це у нашій утиліті Переходимо до класу InitiateUtils
package ru.java.rush.utils;


import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;
import ru.java.rush.entities.FruitEntity;
import ru.java.rush.services.FruitService;

import java.util.List;

@Service
public class InitiateUtils implements CommandLineRunner {

    private final FruitService fruitService;

public InitiateUtils (FruitService fruitService) {//незабываем конструктор для внедрения
    this. fruitService = fruitService;
}

    @Override
    public void run(String... args) throws Exception {

//создаем несколько сущностей фруктов, через сеттеры заполняем поля
        FruitEntity fruitEntity1 = new FruitEntity();
        fruitEntity1.setFruitName("fruit1");
        fruitEntity1.setProviderCode(1);

        FruitEntity fruitEntity2 = new FruitEntity();
        fruitEntity2.setFruitName("fruit2");
        fruitEntity2.setProviderCode(2);

        FruitEntity fruitEntity3 = new FruitEntity();
        fruitEntity3.setFruitName("fruit3");
        fruitEntity3.setProviderCode(3);

//с помощью переменной сервиса вызываем методы сохранения в базу, по разу для одного об'єкта
        fruitService.save(fruitEntity1);
        fruitService.save(fruitEntity2);
        fruitService.save(fruitEntity3);

//здесь вытаскиваем базу обратно
        List<FruitEntity> all = fruitService.getAll();

//и выводим что получилось
        for (FruitEntity entity : all) {
            System.out.println(entity);
        }
    }
}
Висновок у консоль: FruitEntity(id=1, fruitName=fruit1, providerCode=1) FruitEntity(id=2, fruitName=fruit2, providerCode=2) FruitEntity(id=3, fruitName=fruit3, providerCode=3 ) закінчити. "Секундочку!" - Вигукне найуважніший читач - "А де ж тут все-таки Hibernate?" А Hibernate виступає тут у ролі борця невидимого фронту, він зробив дуже важливу річ: створив нам структури БД. Ось коли ми в "сутності" написали поля, і помітабо потрібними інструкціями, Hibernate зробив свою справу. Насправді, при розробці на промі ви навряд займатиметеся структурою БД, все вже буде створено і розгорнуто за вас. Але в таких невеликих проектах Hibernate зі своїм умінням робити структури БД просто не замінимо, звичайно, це його не єдиний плюс,(У цьому проекті ми не будемо їх використовувати). Давайте привітаємося з цим скромним трудягою: зайдемо в IDEA у структуру проекту (зліва таке дерево з папочками та файликами), там знайдемо External Libraries розкриємо його та серед інших бібліотек побачимо
Maven: org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
Maven: org.hibernate.validator:hibernate-validator:6.0.17.Final
Maven: org.hibernate:hibernate-core:5.4.6.Final
Зокрема Hibernate був викачаний для нас мавеном і
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Насправді містер Hiber провів ще одну приховану операцію, але про неї поговоримо в наступній статті. Ось тепер точно все. Як тренування пропоную вам самим реалізувати метод saveAll() для FruitEntity , який збереже всі сутності в базу за один раз. Далі дивимося як скоротити код проекту за допомогою бібліотеки Lombok
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ