СОДЕРЖАНИЕ ЦИКЛА СТАТЕЙ
Сегодня мы доразбираем работу с базой в рамках нашего проекта. Если вы все делали правильно, то у вас должен быть pom вот с такими зависимостями:
Знаете кого мы давно не вспоминали? Это application.yml, мы поговорили не много о нем в одной из прошлых статей. А теперь давайте возьмем и удалим его! Да, да delete и все!
Если мы запустим сейчас проект, то все будет работать как и раньше, попробуйте это сделать. Это произошло по тому, что Spring сам сконфигурировался на дефолных настройках. Теперь надо вернуть наш ямл файл обратно в папку ресурсы, он нам еще пригодиться:
application.yml (наименование должно быть таким же)
Просто называете метод как будто составляете запрос:
findById(Integer id) - найдет объект по id;
countFruitEntityByFruitName(String name) - посчитает количество фруктов с определенным именем;
Это запросы, генерируемые по имени метода обязательно почитайте про них и реализуйте в классе FruitService метод between(Integer from, Integer to) для поиска List<FruitEntity> по значениям поля provider_code входящим в определенный интервал, результат работы выведете в консоль. Например: найти все фрукты, у которых номер поставщика находиться между 5 и 7. Не торопитесь читать дальше, пока не реализуете метод, это не долго.
Как вы могли прочитать в статье про запросы по имени метода: «Все запросы так не напишешь, но простые можно.»
Для более сложных запросов используется аннотация @Query и вместо SQL здесь используется JPQL(тоже возмите на заметку эту статью), для нашего проекта можно сделать JOIN запросы, так:
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
И вот такая структура проекта:

spring:
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:test;
username: sa
password:
h2:
console:
enabled: true
jpa:
hibernate.ddl-auto: create
generate-ddl: true
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
В прошлый раз мы с помощью методов интерфейса JpaRepository<> реализовали несколько запросов к базе:
//сохранить одну запись в таблицу фруктов
public void save(FruitEntity fruitEntity){
fruitRepository.save(fruitEntity);
}
//получить все записи из таблицы фруктов
public List<FruitEntity> getAll(){
return fruitRepository.findAll();
}
//сохранить несколько записей в таблицу фруктов
public void saveAll(List<FruitEntity> fruits){
fruitRepository.saveAll(fruits);
}
Если вы почитали про SQL, как я вас просил в прошлый раз, то вы должны знать, что такие действия с БД должны выполняться с помощью запросов SQL . Но в проекте на это нет и намека, даже в логах консоли нет ни чего похожего.
Давайте их найдем, открываем application.yml, находим там срочку show-sql: (показать sql) и меняем false на true. Запускаем проект и смотрим в консоль, логи наполнились новыми записями очень похожими на SQL, на самом деле большинство из них понять не мудрено, например:
Hibernate: drop table fruit_table if exists //удалить таблицу fruit_table если она есть
Hibernate: drop table provider_table if exists
Hibernate: create table fruit_table (id_fruit integer not null, fruit_name varchar(255), provider_code integer, primary key (id_fruit))//создать таблицу fruit_table с полями id_fruit тип integer not null, fruit_name тип varchar(255), provider_code тип integer, назначить первичным ключем поле id_fruit
Hibernate: create table provider_table (id_provider integer not null, provider_name varchar(255), primary key (id_provider))
Но вот эта запись, может вызвать много вопросов из-за своих знаков вопросов:
Hibernate: insert into fruit_table (fruit_name, provider_code, id_fruit) values (?, ?, ?)
Давайте мыслить логически: Во-первых мы видим слово Hibernate, значит этот скрытный парень приложил тут свою мохнатую лапу. Прочитав про него в интернете, мы узнаем, что мистер Hiber это реализация ORM-модели. Объектно-реляционная модель описывает отношения между программными объектами и записями в БД.
Зафиксировали эту мысль, продолжаем мыслить логически:
С одной стороны, у нас есть объект FruitEntity у него есть три поля: Integer id; String fruitName; Integer providerCode.
С другой стороны, у нас есть таблица в БД fruit_table с полями id_fruit тип integer, fruit_name тип varchar(255), provider_code тип integer.
Грубо говоря Hibernate берет объект FruitEntity, вытаскивает значения полей объекта и записывает их в соответствующие поля таблицы.
У меня к вам вопрос: Смотрите, в классе InitiateUtils мы реализовали заполнение таблицы фруктов, но почему то мы тут задали значение только двум полям, где третье?
new FruitEntity()
.setFruitName("Fruit1")//раз
.setProviderCode(Math.abs(new Random().nextInt() % 10)),//два
//три???
Уверен вы разберетесь в этом сами, к тому же мы мельком касались этого вопроса в позапрошлой статье. Для начала разберитесь какого поля тут нет, а дальше вы все поймете.
Ну что ж, Hiber молодец, нагенерировал за нас кучу запросов.
Но и мы не лыком шиты, давайте в классе FruitService реализуем еще несколько методов из интерфейса JpaRepository<>
//возвращает запись из таблицы по id
public Optional<FruitEntity> getById(Integer id){
return fruitRepository.findById(id);
}
//удаляет запись из таблицы по id
public void delById(Integer id){
fruitRepository.deleteById(id);
}
//возвращает true или false при поиске в таблице Фруктов объекта который соответствует типу FruitEntity или принадлежит к типу объекта который наследуется от FruitEntity
public Boolean exist(Example<? extends FruitEntity> example){
return fruitRepository.exists(example);
}
Реализуйте такие же методы в классе ProviderService. Затем используйте их в классе InitiateUtils для FruitEntity и ProviderEntity, результат выведете в консоль. (Кстати, если вы не знали то быстро написать “System.out.println()” можно если набрать sout и нажать ентер, таже штука работает с “public static void main(String[] args){}” достаточно набрать psvm ну и так далее).
Я думаю вы уже справились и мы готовы идти дальше. Давайте зайдем в интерфейс FruitRepository и начнем в нем набирать (именно набирать а не копировать) следующий метод: List<FruitEntity> f
Должна получиться следующая штука

package ru.java.rush.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.java.rush.entities.FruitEntity;
import java.util.List;
@Repository
public interface FruitRepository extends JpaRepository<FruitEntity,Integer> {
@Query("select f.fruitName, p.providerName from FruitEntity f left join ProviderEntity p on f.providerCode = p.id")
List<String> joinSting();
}
Стандартный SQL запрос был бы таким:
"select fruit_table.fruit_name, provider_table.provider_name from fruit_table left join provider_table on fruit_table.provider_сode = provider_table.id".
Здесь легко можно установить соответствие:
fruit_table это FruitEntiy f, где FruitEntiy это тип переменной, f это ее наименование, то есть SQL работает с таблицами и полями, а JPQL с объектами и их полями.
Опять же fruit_table.fruit_name это f.fruitName;
Так как мы работаем с объектами то можем и объект вывести:
Напишем еще один метод FruitRepository
@Query("select f from FruitEntity f join ProviderEntity p on f.providerCode = p.id")
List<FruitEntity> joinFruit();
Реализуем оба метода в классе FruitService
public List<String> joinString(){
return fruitRepository.joinSting();
}
public List<FruitEntity> joinFruit(){
return fruitRepository.joinFruit();
}
Звучит не плохо, но все равно для сложных запросов используют старый добрый SQL
@Query(
value = "select fruit_table.fruit_name, provider_table.provider_name from fruit_table join provider_table on fruit_table.provider_code = provider_table.id_provider", //по идее эту портянку надо засунуть в какой нибудь Enum
nativeQuery = true) //нужно только пометить только nativeQuery = true
ListList<String> joinSqlFruit();
И используем их всех в классе InitiateUtils
System.out.println("\nТаблица фруктов и их поставщиков");
for (String join : fruitService.joinString()) {
System.out.println(join);
}
System.out.println("\nТаблица фруктов и их поставщиков");
for (FruitEntity join : fruitService.joinFruit()) {
System.out.println(join);
}
System.out.println("\nТаблица фруктов и их поставщиков");
for (String join : fruitService.joinSqlFruit()) {
System.out.println(join);
}
Запускаем проект и видим в консоли новые логи:
Таблица фруктов и их поставщиков
Fruit1,null
Fruit2,Provider5
Fruit3,Provider2
Fruit4,Provider5
Fruit5,null
Fruit6,null
Fruit7,null
Fruit8,null
Fruit9,null
Таблица фруктов и их поставщиков
FruitEntity(id=2, fruitName=Fruit2, providerCode=5)
FruitEntity(id=3, fruitName=Fruit3, providerCode=2)
FruitEntity(id=4, fruitName=Fruit4, providerCode=5)
Таблица фруктов и их поставщиков
Fruit2,Provider5
Fruit3,Provider2
Fruit4,Provider5
Да, если вам уже надоели "псевдо" и просто SQL запросы в консоли, можете в ямл файле вернуть false на место.
Почему в первой таблице есть null, это вы узнаете если почитаете про JOIN SQL .
И так, с запросами к БД мы закончили, я уверен, что у вас осталось много вопросов, но я надеюсь, что вы будете искать на них ответы, пути поиска я попытался подсветить.
Давайте попытаемся с резюмировать все что мы узнали за это время:
1. Можно запустить веб сервер на Spring и это не сложно.
2. Для понимания как это все работает необходимо погрузиться в теорию.
Про книгу
Статья про спринг
Статья про полезные вещи
3. А что бы понимание перешло в физический код надо кодить, и прежде чем идти дальше, набейте руку на простых проектах на spring-boot.
И лучше не копировать написанный код, а переписывать его.
Я выложу проект, который мы с вами пилили сюда, но буду надеяться на вашу сознательность и уверен вы не будете бездумно копипастить.
Ссылка на репозиторий
git clone https://FromJava@bitbucket.org/FromJava/jd.git
Для тех кто не знает как пользоваться этой ссылкой
Рекомендую реализовать два тренировочных проекта:
Проект про покраску машин:
Первый класс:
CarEntity{
Integer id;
String modelName;
String color;
}
Второй класс:
ColorEntity{
Integer id;
String color;
Integer price;
}
Наполните базу (придумайте реалистичные названия так будет легче для понимания), реализуйте: , entity, repositories, services, создайте стандартные и межтабличные запросы (Сколько стоит покраска BMW в красный цвет? Какой цвет самый дорогой? Напишите модели в консоль в алфавитном порядке и т.д.);
Проект про библиотеку:
Первый класс:
BookEntity{
Integer id;
String nameBook;
Integer yearCreat;
Integer autorId;
}
Второй класс:
AutorEntity{
Integer id;
String firstNameAutor;
String lastNameAutor;
}
Наполните базу (придумайте реалистичные названия так будет легче для понимания), реализуйте: entity, repositories, services, создайте стандартные и межтабличные запросы (Кто написал какую книгу? Какая книга написана раньше всех? Какие книги были написаны с 1800 по 1900? Кто из авторов написал больше всего книг?);
Примеры для заполнения базы проекта "Библиотека"
Таблица книг
BookEntity(id=1, nameBook=Горе от ума, yearCreat=1824, authorId=1)
BookEntity(id=2, nameBook=Война и мир, yearCreat=1863, authorId=2)
BookEntity(id=3, nameBook=Мцыри, yearCreat=1838, authorId=3)
BookEntity(id=4, nameBook=Евгений Онегин, yearCreat=1833, authorId=4)
Таблица авторов
AuthorEntity(id=1, firstNameAuthor=Александр, lastNameAuthor=Грибоедов)
AuthorEntity(id=2, firstNameAuthor=Лев, lastNameAuthor=Толстой)
AuthorEntity(id=3, firstNameAuthor=Михаил, lastNameAuthor=Лермонтов)
AuthorEntity(id=4, firstNameAuthor=Александр, lastNameAuthor=Пушкин)
Всем удачи, еще увидимся!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ