СОДЕРЖАНИЕ ЦИКЛА СТАТЕЙ
Продолжаем наш проект.
Как создать веб проект.
Подключаем базу к своему веб проекту.
В этот раз букв будет больше, пятью минутами не обойдемся.
В прошлой статье я советовал почитать про 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
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ