Сьогодні зануримось у світ баз даних, проєктування сутностей і інтеграції всього цього в наш додаток. Якщо пам'ятаєте, у попередніх лекціях ми вже вивчали Spring Data JPA, ORM-аніотації, такі як @Entity, @Id, @Column, побудували репозиторії і навіть трохи попрацювали з базовими CRUD-операціями. Тепер час застосувати знання на практиці і зібрати докупи теорію і логіку для фінального проєкту.
1. Проєктування структури бази даних
Ми вже трохи говорили про те, як проєктувати базу даних у кількох попередніх лекціях. Тепер заглибимось у цей процес.
Часто саме від коректного проєктування бази даних залежить успіх вашого додатку. Уявіть собі історію, де дані неструктуровані або їх неможливо оперативно діставати. Це майже як розбиратися в купі коду без коментарів.
На практиці треба враховувати:
- Логічні зв'язки між даними.
- Оптимізацію запитів.
- Уникнення надмірності.
Щоб краще візуалізувати процес, створимо ER-діаграму нашої бази даних. Для фінального проєкту припустимо, що ми робимо додаток для управління задачами (Task Management System), де користувачі можуть створювати задачі, ставити дедлайни і відстежувати їхній статус.
ER-діаграма
На основі вимог база даних може виглядати так:
USER
---------------------
id (PK)
username
email
password
role
TASK
---------------------
id (PK)
title
description
deadline
status
user_id (FK -> USER.id)
Як видно, у нас два основні об'єкти: User і Task. Вони пов'язані між собою (User може мати багато Tasks, але кожна Task прив'язана лише до одного User).
- USER:
- Містить дані користувача (логін, email, пароль, роль тощо).
- TASK:
- Містить дані задачі (заголовок, опис, дедлайн, статус, посилання на власника).
2. Імплементація сутностей у коді
Переходимо до написання моделі даних у Spring-додатку. Ми будемо використовувати JPA-аніотації для визначення сутностей.
Клас User
package com.example.taskmanagement.model;
import jakarta.persistence.*;
import java.util.List;
@Entity
@Table(name = "users") // Вказуємо явно ім'я таблиці: в SQL "user" - зарезервоване слово
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // Автоінкремент для первинного ключа
private Long id;
@Column(nullable = false, unique = true) // Логін має бути унікальним
private String username;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String role; // Можна вказати роль, наприклад ADMIN або USER
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Task> tasks; // Зв'язок із задачами, ліниве (lazy) завантаження
// Геттери, сеттери, конструктори...
}
Клас Task
Основні моменти:
- Зв'язок
@OneToManyв класіUserдає змогу отримати всі задачі користувача. - Зв'язок
@ManyToOneв класіTaskдозволяє кожній задачі бути прив'язаною до одного користувача. - Ми використовуємо
LAZYзавантаження, щоб при запиті користувача (або задачі) не тягнути одразу пов'язану сутність з бази.
3. Ініціалізація бази даних
Тепер, коли сутності готові, давайте створимо скрипти для заповнення бази тестовими даними. Можна використовувати H2 (локально) або PostgreSQL (для продакшн-середовища).
Скрипт для створення таблиць
Створимо файл schema.sql:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL
);
CREATE TABLE tasks (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
deadline DATE NOT NULL,
status VARCHAR(20) NOT NULL,
user_id BIGINT NOT NULL,
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id)
);
Скрипт для наповнення тестовими даними
Файл data.sql:
-- Користувачі
INSERT INTO users (username, email, password, role) VALUES
('john_doe', 'john@example.com', 'password123', 'USER'),
('admin', 'admin@example.com', 'admin123', 'ADMIN');
-- Завдання
INSERT INTO tasks (title, description, deadline, status, user_id) VALUES
('Complete homework', 'Finish math and science homework', '2023-12-01', 'PENDING', 1),
('Fix server', 'Resolve critical issue on production server', '2023-11-25', 'IN_PROGRESS', 2);
Налаштування application.yml (H2 Database) Якщо хочете використовувати H2 і протестувати локально:
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create
show-sql: true
При запуску Spring Boot додаток автоматично створить таблиці, використовуючи анотовані сутності.
4. Перевірка роботи на практиці
Давайте напишемо репозиторії і протестуємо додавання даних.
Репозиторій для User
package com.example.taskmanagement.repository;
import com.example.taskmanagement.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
Репозиторій для Task
package com.example.taskmanagement.repository;
import com.example.taskmanagement.model.Task;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface TaskRepository extends JpaRepository<Task, Long> {
List<Task> findByUserId(Long userId);
}
Кілька рядків для тесту. Створіть тестовий сервіс або метод, щоб перевірити:
@Autowired
private UserRepository userRepository;
@Autowired
private TaskRepository taskRepository;
public void testDatabase() {
User user = userRepository.findByUsername("john_doe");
System.out.println("Користувач: " + user.getUsername());
List<Task> tasks = taskRepository.findByUserId(user.getId());
tasks.forEach(task -> System.out.println("Завдання: " + task.getTitle()));
}
Якщо все налаштовано правильно, ви повинні побачити дані з вашого data.sql в консолі.
На цьому етапі ви вже бачите основи роботи з базами в Spring-додатку: від опису сутностей до ініціалізації даних і створення зв'язків. Усе готове для додавання API на основі цих даних і реалізації бізнес-логіки в проєкті.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ