Сегодня мы с вами погрузимся в мир баз данных, проектирования сущностей и интеграции этого всего в наше приложение. Если вы помните, в предыдущих лекциях мы уже изучили 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: " + user.getUsername());
List<Task> tasks = taskRepository.findByUserId(user.getId());
tasks.forEach(task -> System.out.println("Task: " + task.getTitle()));
}
Если всё настроено верно, вы должны увидеть данные из вашего data.sql в консоли.
На этом этапе вы уже видите основы работы с базами в Spring-приложении: от описания сущностей до инициализации данных и создания связей. Всё готово для добавления API на основе этих данных и обеспечения логики в проекте.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ