Ответ
Транзакция — это логическая единица работы с базой данных, которая состоит из одной или нескольких операций. Она выполняется по принципу "всё или ничего": либо все изменения фиксируются, либо все откатываются, что гарантирует целостность данных.
Свойства ACID:
- Atomicity (Атомарность): Транзакция выполняется полностью или не выполняется вовсе.
- Consistency (Согласованность): Транзакция переводит базу данных из одного валидного состояния в другое.
- Isolation (Изолированность): Параллельные транзакции не влияют друг на друга. Уровни изоляции (READ_COMMITTED, REPEATABLE_READ и др.) определяют степень этого влияния.
- Durability (Долговечность): После подтверждения (commit) изменения сохраняются даже при сбое системы.
Практический пример на JDBC:
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false); // Начало транзакции (отключение auto-commit)
PreparedStatement stmt1 = conn.prepareStatement(
"UPDATE accounts SET balance = balance - ? WHERE id = ?");
stmt1.setBigDecimal(1, new BigDecimal("100.00"));
stmt1.setInt(2, 1);
stmt1.executeUpdate(); // Списание со счета 1
PreparedStatement stmt2 = conn.prepareStatement(
"UPDATE accounts SET balance = balance + ? WHERE id = ?");
stmt2.setBigDecimal(1, new BigDecimal("100.00"));
stmt2.setInt(2, 2);
stmt2.executeUpdate(); // Зачисление на счет 2
conn.commit(); // Фиксация всех изменений
} catch (SQLException e) {
conn.rollback(); // Откат всех изменений при ошибке
throw e;
} finally {
conn.setAutoCommit(true); // Восстановление режима по умолчанию
conn.close();
} Ответ 18+ 🔞
Слушай, ну вот объясняю тебе про транзакции, как будто ты в баре сидишь и пытаешься деньги другу перевести, а у тебя уже пятый бокал. Представь, что база данных — это твой кошелёк и кошелёк твоего кореша. Транзакция — это когда ты говоришь: «Так, ща я ему сотку перекину». И это всё или нихуя. Либо ты ему отправил и у тебя списалось, а у него прибавилось, либо, если что-то пошло не так (интернет сдох, ты опечатался), то всё как было. Никаких промежуточных состояний, типа «у тебя списалось, а он нихуя не получил». Это и есть атомарность, ёпта.
А теперь про эти ваши ACID, блядь. Запоминай, как мантру, а то потом будешь в коде жопу рвать.
- Атомарность (Atomicity) — это как раз про «всё или ничего». Как в том анекдоте: «Или грудь в крестиках, или жопа в бинтах». Третьего не дано.
- Согласованность (Consistency) — ну, тут всё логично. База не должна превратиться в сюрреалистичный пиздец после твоих манипуляций. Если у тебя в таблице
accountsесть ограничение, что баланс не может быть отрицательным, то транзакция, которая попытается уйти в минус, просто отъедет нахер с ошибкой. База останется в валидном состоянии, в рот меня чих-пых. - Изолированность (Isolation) — это, блядь, самое интересное. Представь, что вы с другом одновременно лезете в один холодильник за последним пивом. Кто первый схватит, тот и выпил. В базах та же хуйня: если две транзакции лезут в одни и те же данные, могут быть гонки. Поэтому придумали уровни изоляции —
READ_COMMITTED,REPEATABLE_READи прочие. Это как правила: «Сначала посмотри, свободен ли холодильник, а потом уже лезь». Чем строже уровень, тем меньше аномалий, но тем всё медленнее работает, потому что все друг другу мешают. Пиздец, короче. - Долговечность (Durability) — это когда ты уже подтвердил перевод (
commit), и даже если у тебя сервер хуяк и упал, после перезагрузки эти деньги всё равно будут переведены. Они как будто в каменную скрижаль записаны, а не на салфетке в баре.
А вот, смотри, как это выглядит в коде на JDBC. Это ж как инструкция по выживанию, блядь:
Connection conn = dataSource.getConnection();
try {
// Выключаем эту хуйню auto-commit. Теперь мы сами рулим.
conn.setAutoCommit(false);
// Готовим первый удар: списываем сотку с первого счёта.
PreparedStatement stmt1 = conn.prepareStatement(
"UPDATE accounts SET balance = balance - ? WHERE id = ?");
stmt1.setBigDecimal(1, new BigDecimal("100.00"));
stmt1.setInt(2, 1);
stmt1.executeUpdate(); // Пшшш, деньги полетели... но пока только в буфере.
// Второй удар: зачисляем на второй счёт.
PreparedStatement stmt2 = conn.prepareStatement(
"UPDATE accounts SET balance = balance + ? WHERE id = ?");
stmt2.setBigDecimal(1, new BigDecimal("100.00"));
stmt2.setInt(2, 2);
stmt2.executeUpdate(); // И эти тоже в буфере.
// Всё прошло гладко? Отлично, хуяк — commit! Теперь изменения реально записались.
conn.commit();
} catch (SQLException e) {
// Ага, что-то пошло не так? Может, на втором счету ID не найден?
// Тогда откатываем ВСЁ, что наделали в этой транзакции. И списание, и зачисление. Нахуй.
conn.rollback();
throw e; // И кидаем ошибку дальше, пусть разбираются.
} finally {
// Обязательно включаем auto-commit обратно, чтобы не поймать сюрприз в другом месте.
conn.setAutoCommit(true);
conn.close(); // И закрываем соединение, а то ресурсы кончатся.
}
Вот и вся магия. Главное — не забывать про setAutoCommit(false), а то будет как в том анекдоте: каждый апдейт сам по себе коммитится, и если второй упадёт, то первый уже не откатить. Получится, ты деньги списал, а другу нихуя не пришло. А это, ясень пень, пиздец и разборки на почве.