Skip to content

Створення та використання Active Record

Мета: Навчитися використовувати патерн Active Record для взаємодії з базою даних у застосунках. Ознайомитися з основними операціями CRUD (Create, Read, Update, Delete) за допомогою Active Record.


Завдання

Необхідно реалізувати систему управління статтями у блозі, де є дві сутності:

  1. Article (Стаття)
  2. Comment (Коментар)

Зв'язок

Кожна стаття може мати кілька коментарів (зв’язок "один до багатьох").

  • Використайте патерн Active Record.
  • Можна використовувати такі мови програмування: Java, C#, C++, Kotlin, PHP, JavaScript.

Хід роботи

Демонстрацію патерну Active Record буде проведено на проєкті написаного мовою Java, без використання фреймворків по типу Spring, для чистоти.

Що таке Active Record?

Active Record — це патерн проектування, в якому об'єкт, що представляє рядок у звичаній таблиці бази даних, інкапсулює доступ до бази даних, додає логіку домену до цих даних.

Основні характеристики:

  1. Клас = Таблиця: Кожен клас відповідає таблиці в БД.
  2. Екземпляр = Рядок: Кожен об'єкт класу відповідає конкретному рядку в таблиці.
  3. Методи = SQL операції: Об'єкт має методи save(), update(), delete(), які виконують відповідні SQL запити.
  4. Статичні методи: Для пошуку (findById, findAll) використовуються статичні методи класу.

🏗️ Архітектура та Потік Даних

У нашій реалізації ми маємо дві основні сутності: Article (Стаття) та Comment (Коментар).

classDiagram
    class ActiveRecord {
        <<abstract>>
        +save()*
        +delete()*
        #getConnection() Connection
    }

    class Article {
        -Long id
        -String title
        -String content
        -String author
        -Timestamp createdAt
        +save()
        +delete()
        +getComments() List~Comment~
        +static findById(Long) Article
        +static findAll() List~Article~
    }

    class Comment {
        -Long id
        -Long articleId
        -String author
        -String content
        -Timestamp createdAt
        +save()
        +delete()
        +static findById(Long) Comment
        +static findAll() List~Comment~
        +static findByArticleId(Long) List~Comment~
    }

    ActiveRecord <|-- Article
    ActiveRecord <|-- Comment
    Article "1" --> "*" Comment : має

Потік виконання (на прикладі створення статті):

  1. Створюється об'єкт new Article(...).
  2. Викликається метод .save().
  3. Всередині save() формується SQL INSERT.
  4. Через DatabaseConnection отримується з'єднання.
  5. Запит виконується, і згенерований ID присвоюється об'єкту.

🛠️ Нюанси Реалізації

Ця реалізація є навчальною та спрощеною. Основні рішення та причини їх прийняття:

  1. Прямий JDBC (Java Database Connectivity):

    • Чому: Щоб показати "магію", яка відбувається під капотом ORM.
    • Як це працює: Ми вручну пишемо SQL запити (SELECT * FROM...) та мапимо ResultSet у поля об'єкта.
  2. ActiveRecord як базовий клас:

    • Чому: Для винесення спільної логіки (отримання з'єднання) та визначення контракту (save, delete).
    • Обмеження: У Java статичні методи не можна перевизначити (override) так само як звичайні, тому методи find... доводиться дублювати в кожному класі.
  3. Управління транзакціями:

    • Поточний стан: Кожна операція save() — це окрема транзакція (autocommit).
    • Недолік: Немає можливості відкотити зміни, якщо, наприклад, збереження статті пройшло, а коментаря — ні.
  4. Lazy Loading (Ліниве завантаження) коментарів:

    • Метод article.getComments() робить запит до БД тільки в момент виклику, а не під час завантаження статті. Це економить ресурси, якщо коментарі не потрібні.

🚀 Як зробити "по-дорослому"? (Production Ready)

Для реальних великих проектів патерн Active Record у чистому вигляді в Java використовується рідко. Зазвичай використовують потужніші інструменти:

1. Spring Data JPA / Hibernate (Data Mapper Pattern)

Найпопулярніший підхід у світі Java. * Чому краще: * Автоматична генерація SQL (не треба писати руками). * Керування транзакціями (@Transactional). * Кешування першого та другого рівня. * Захист від SQL Injection "з коробки" (хоча ми теж використовуємо PreparedStatement). * Відмінність: Сутності (Entity) — це просто дані (POJO), а логіка збереження винесена в Репозиторії (Repository).

2. JOOQ (Java Object Oriented Querying)

Якщо ви любите SQL і хочете повний контроль, але з типізацією Java. * Дозволяє писати SQL-подібний код типу: create.selectFrom(ARTICLE).where(ARTICLE.ID.eq(1)).fetch().

3. Panache (Quarkus)

Це фреймворк, який якраз реалізує Active Record pattern у світі Java Enterprise (на базі Hibernate). * Виглядає так: Article.findById(1), article.persist(). * Це ідеальний варіант, якщо вам подобається стиль Active Record, але потрібна потужність Hibernate.

4. MyBatis

Дозволяє винести всі SQL запити в XML файли або анотації, відокремлюючи SQL від Java коду.


Висновок

Виконання практичної роботи дозволило зрозуміти принцип взаємодії з базою даних згідно патерну Active Record у якому запити відбуваються через сутність.