← Назад к вопросам

Как работает ORM система в JPA?

1.0 Junior🔥 171 комментариев
#ORM и Hibernate

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает ORM система в JPA

JPA (Java Persistence API) — это стандартный фреймворк для отображения объектов Java на таблицы реляционной базы данных. ORM (Object-Relational Mapping) в JPA решает проблему несоответствия между объектно-ориентированным кодом и реляционными данными в БД. Рассмотрим основные механизмы работы.

Основной принцип ORM

Основа ORM — это отображение объектов на строки таблиц и полей объектов на колонки:

// Java объект
public class User {
    private Long id;
    private String name;
    private String email;
}

// Отображается на таблицу
/*
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(255),
    email VARCHAR(255)
);
*/

Аннотации JPA (основные)

import jakarta.persistence.*;

@Entity                           // Отмечает класс как сущность
@Table(name = "users")           // Определяет таблицу в БД
public class User {
    
    @Id                           // Первичный ключ
    @GeneratedValue(strategy = GenerationType.IDENTITY) // Auto-increment
    private Long id;
    
    @Column(name = "full_name", nullable = false, length = 100)
    private String name;          // Отображается на колонку full_name
    
    @Column(unique = true)        // Уникальное ограничение
    private String email;
    
    @Transient                    // Не отображается на БД
    private String temporaryData;
    
    @OneToMany(mappedBy = "user")
    private List<Post> posts;     // Связь один-ко-многим
    
    @ManyToOne
    @JoinColumn(name = "department_id")
    private Department department; // Связь многие-к-одному
}

Жизненный цикл сущности

Каждая сущность JPA проходит через четыре состояния:

1. Transient (Временное состояние)

User user = new User(); // Объект создан, но не управляется JPA
user.setName("John");
user.setEmail("john@example.com");
// Это обычный Java объект, изменения не отслеживаются

2. Managed (Управляемое состояние)

// После сохранения или получения из БД
User managedUser = entityManager.persist(user);  // Теперь объект управляем
// или
User user = entityManager.find(User.class, 1L); // Полученный из БД

3. Detached (Отсоединённое состояние)

session.close(); // Сессия закрыта
// Объект больше не управляется, но хранит данные
User detachedUser = user; // Все еще имеет значения полей

4. Removed (Удаленное состояние)

entityManager.remove(managedUser); // Объект помечен на удаление
// После commit() будет удален из БД

Кэш (Persistence Context)

JPA использует кэш первого уровня (Persistence Context) в рамках сессии:

@Service
public class UserService {
    @Autowired
    private EntityManager em;
    
    @Transactional
    public void example() {
        User user1 = em.find(User.class, 1L); // Запрос в БД
        User user2 = em.find(User.class, 1L); // НЕ запрос, возврат из кэша
        
        // user1 == user2 (одно и то же объект)
        assert user1 == user2;
    }
}

Это повышает производительность, так как избегаются лишние запросы к БД.

Отслеживание изменений (Dirty Checking)

JPA отслеживает изменения управляемых объектов и автоматически обновляет БД при commit():

@Transactional
public void updateUser(Long id) {
    User user = em.find(User.class, id);
    user.setName("Updated Name");           // Никакого явного обновления
    
    // При конце транзакции JPA сама выполнит:
    // UPDATE users SET full_name = ? WHERE id = ?
}

Этот механизм называется Dirty Checking — JPA сравнивает текущее состояние объекта с изначальным и генерирует необходимые UPDATE запросы.

Связи между сущностями

OneToMany и ManyToOne

@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Post> posts;   // Один пользователь, много постов
}

@Entity  
public class Post {
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;          // Много постов, один пользователь
}

ManyToMany

@Entity
public class Student {
    @ManyToMany
    @JoinTable(name = "student_courses",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private List<Course> courses;
}

JPQL и Native Queries

Для выборки данных используются запросы:

// JPQL (платформо-независимый)
Query query = em.createQuery(
    "SELECT u FROM User u WHERE u.email = :email"
);
query.setParameter("email", "john@example.com");
User user = (User) query.getSingleResult();

// Native SQL
Query nativeQuery = em.createNativeQuery(
    "SELECT * FROM users WHERE email = ?",
    User.class
);
nativeQuery.setParameter(1, "john@example.com");
User user = (User) nativeQuery.getSingleResult();

Lazy Loading vs Eager Loading

@Entity
public class User {
    @OneToMany(fetch = FetchType.LAZY) // Загружается при обращении
    private List<Post> posts;
    
    @ManyToOne(fetch = FetchType.EAGER) // Загружается со строки User
    private Department department;
}

Lazy Loading (по умолчанию для List, Set) — экономит память, но может привести к N+1 проблеме. Eager Loading — загружает все данные сразу, потребляет больше памяти, но избегает лишних запросов.

Каскадные операции

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Post> posts;

// Теперь при удалении User автоматически удалятся все его Post
entityManager.remove(user);

ОRM система JPA скрывает сложность отображения объектов на реляционные данные, позволяя разработчикам работать с привычными объектами вместо писания SQL запросов вручную."