Что такое сессия (Session) в Hibernate?

Ответ

Сессия (org.hibernate.Session) — это центральный, короткоживущий объект в Hibernate, который представляет собой обертку вокруг JDBC-соединения и является точкой входа для выполнения операций с базой данных. Это не потокобезопасный объект, который обычно ассоциируется с одним запросом или транзакцией (паттерн "session-per-request").

Ключевые обязанности Session

  1. Кэш первого уровня (L1 Cache): Каждая сессия имеет свой собственный кэш объектов. Повторные обращения к одному и тому же объекту в рамках одной сессии не вызывают лишних SQL-запросов.
  2. Управление жизненным циклом объектов: Сессия отслеживает состояние объектов (transient, persistent, detached, removed).
  3. Координация запросов: Преобразует операции с объектами (save, update) в SQL-запросы.
  4. Обеспечение ленивой загрузки (Lazy Loading): Позволяет отложенно загружать связанные коллекции и прокси-объекты.

Пример типичного использования

// Получение сессии из фабрики (обычно через ThreadLocal)
Session session = sessionFactory.openSession();
Transaction transaction = null;

try {
    transaction = session.beginTransaction();

    // CRUD операции
    User newUser = new User("Alice"); // Transient state
    session.save(newUser); // Persistent state, INSERT запланирован

    User user = session.get(User.class, 1L); // SELECT, объект закеширован в L1
    user.setName("Bob"); // UPDATE запланирован (изменение отслеживается)

    session.delete(user); // DELETE запланирован

    transaction.commit(); // Все запросы выполняются здесь

    // После commit/close объект становится detached
    System.out.println(newUser.getId()); // ID доступен после сохранения

} catch (Exception e) {
    if (transaction != null) transaction.rollback();
    throw e;
} finally {
    session.close(); // Критически важно закрывать сессию!
}

Важные принципы работы

  • Короткий срок жизни: Сессия должна открываться непосредственно перед операцией и закрываться сразу после. Не храните ее открытой.
  • Не потокобезопасна: Никогда не используйте одну сессию в нескольких потоках.
  • Связь с транзакцией: Границы сессии и транзакции часто совпадают, но это не обязательно.
  • Отсоединенные объекты (Detached): После session.close() или session.evict() объекты становятся detached. Их изменения не отслеживаются, для повторного сохранения нужен session.update() или session.merge().

Ответ 18+ 🔞

А, слушай-ка, про эту вашу сессию в Хибернейте. Ну, это ж, блядь, как главный дирижёр в оркестре из SQL-запросов, только живёт он, как мотылёк — один запрос и нахуй. Обёртка вокруг соединения с базой, короче. И главное — с ней в нескольких потоках нельзя, она не потокобезопасная, сука! Одна сессия — один запрос, паттерн такой, session-per-request, запомни.

Чем она, эта манда с ушами, занимается?

  1. Кэш свой, блядь, первый уровень. У каждой сессии свой личный закуток в памяти, куда она складывает объекты. Спросил один раз пользователя — она тебе его из базы вытащила и в закуток положила. Спросишь второй раз в этой же сессии — она тебе из закутка подаст, SQL-то новый не пойдёт. Удобно, ёпта!
  2. За объектами следит, как наседка. Она знает, в каком они состоянии: только созданный (transient), уже привязанный к базе (persistent), отвязанный (detached) или на удаление (removed). Вся эта хуйня.
  3. Переводит с языка объектов на язык SQL. Сказал ей session.save(newUser) — она в уме уже строчит INSERT INTO users .... Хитрая жопа.
  4. Ленивую загрузку обеспечивает. Это когда связанные коллекции грузятся не сразу, а только когда ты к ним полез. Чтобы не тащить овердохуища данных просто так.

Вот как это обычно выглядит в коде, смотри:

// Достаём сессию из фабрики (обычно её через ThreadLocal прячут)
Session session = sessionFactory.openSession();
Transaction transaction = null;

try {
    transaction = session.beginTransaction();

    // CRUD операции, вся наша работа тут
    User newUser = new User("Alice"); // Это transient, база про него не знает
    session.save(newUser); // А теперь persistent! INSERT запланирован

    User user = session.get(User.class, 1L); // SELECT, объект теперь в кэше L1
    user.setName("Bob"); // Ёбаный UPDATE запланирован, сессия же следит!

    session.delete(user); // Ну и DELETE, ясное дело

    transaction.commit(); // И вот тут, блядь, ВСЕ запланированные запросы полетели в базу!

    // После commit или close объект становится detached, отвязанным
    System.out.println(newUser.getId()); // ID появится только после сохранения, логично

} catch (Exception e) {
    if (transaction != null) transaction.rollback(); // Откатываемся, если пиздец
    throw e;
} finally {
    session.close(); // ЗАКРЫТЬ, БЛЯДЬ, ОБЯЗАТЕЛЬНО! Не оставляй её висеть!
}

А теперь главные правила, чтобы не обосраться:

  • Живёт недолго. Открыл — сделал дело — закрыл. Не таскай её с собой, как любимую игрушку.
  • Не для всех потоков. Одна сессия — один поток. Иначе будет race condition и прочий пиздец.
  • Часто с транзакцией за ручку. Границы сессии и транзакции обычно совпадают, но не всегда, это тонкость.
  • Отвязанные объекты (Detached). После session.close() или session.evict() объекты становятся как сироты — detached. Их изменения уже не отслеживаются. Хочешь обратно в базу — делай session.update() или session.merge(). Вот такая, блядь, магия.