4.1 Транзакции и целостность базы данных
Обычный режим работы базы данных — это когда к ней каждую минуту приходят тысячи запросов от сотен разных клиентов. При этом часто возникают ситуации, когда к одним и тем же данным идет обращение из разных запросов.
Реже, но, опять-таки, время от времени возникают ситуации, когда один запрос читает некоторую строку, а другой запрос в это время меняет ее. Представь, что будет, если кто-то прочитает строку, которая изменена только наполовину? Ничего хорошего.
Эту проблему решают несколькими способами. Во-первых, можно просто лочить строку, которая меняется. И для чтения, и для записи. Этот способ работает, но скорость базы сильно страдает.
Второй способ — это лочить строку только на запись. При этом все равно будет проблема, когда кто-то попытается прочитать частично измененную строку. Вывод — не должно быть ситуации, когда строка изменена частично.
Поэтому придумали третий способ — транзакции. Транзакция — это группа действий, которые выполняются или все вместе, или ни одного. Не может быть ситуации, когда часть действий выполнилась, а вторая часть — нет. Если не получается внести все изменения, то все уже внесенные откатываются назад.
Любой современный SQL-сервер позволяет изменять данные только в транзакциях. Ты открываешь транзакцию, вносишь любые изменения в любое количество таблиц, и комитишь транзакцию. Затем SQL-сервер пробует внести изменения. Если все просто хорошо, то они добавятся к общей базе данных. Если были проблемы, то все изменения отменятся.
Hibernate тоже использует эту парадигму. Именно поэтому в предыдущей лекции мы увидели, что при попытке сохранить объект Employee в базу сначала открывалась транзакция, а после сохранения – коммитилась.
Мы еще разберем эту тему детальнее, а пока просто знай, зачем транзакции нужны и где они обычно используются.
4.2 Получение объектов
Если Hibernate выполняет запрос на получение данных, то явно транзакцию открывать не нужно. Hibernate сам это сделает, если посчитает нужным: у него есть его настройки, а также настройки SQL-сервера.
Мы же с тобой разберем способы работы с базой данных. И самый простой из них — это получение объекта по его ID. Для этого нужно использовать метод get()
у объекта session. Общий вид такого запроса:
Класс имя = session.get(Класс.class, ID);
Пример:
public User getUserById(Integer id) {
try (Session session = sessionFactory.openSession()) {
User user = session.get(User.class, id);
return user;
}
}
4.3 Сохранение (добавление) объектов
Если ты хочешь сохранить свой объект в базу данных, то на уровне SQL будет выполнен запрос INSERT. Поэтому твои действия нужно выполнять в виде отдельной транзакции. Кроме того, для сохранения лучше использовать метод persist()
объекта session.
Общий вид такого запроса:
session.persist(Объект);
Метод persist()
меняет не только базу, но и сам объект. Все дело в том, что когда мы добавляем объект в базу, то до добавления у этого объекта еще нет своего ID. Ну, обычно так, хотя бывают нюансы. А после добавления у объекта ID уж есть.
public boolean saveUser(User user) {
try (Session session = sessionFactory.openSession()) {
Transaction transaction = session.beginTransaction();
session.persist(user);
transaction.commit();
return true;
}
catch() {
return false;
}
}
Также у объекта Session есть метод save()
, который выполняет аналогичную функцию. Просто метод save()
— это старый стандарт Hibernate, а метод persist()
— это JPA-стандарт.
4.4 Удаление объектов
Если вы хотите удалить существующей объект, то сделать это очень просто. Для этого у объекта session есть специальный метод — remove()
.
Общий вид такого запроса:
session.remove(Объект);
И, конечно же, напишем код с примером:
public boolean removeUser(User user) {
try (Session session = sessionFactory.openSession()) {
Transaction transaction = session.beginTransaction();
session.remove(user);
transaction.commit();
return true;
}
catch() {
return false;
}
}
Зачем так сложно, спросишь ты?
Ну, во-первых, любые изменения базы данных всегда несут за собой различные и не всегда очевидные последствия. А во-вторых, у данного объекта могли быть связанные с ним дочерние объекты и т. п. Так что сценарии удаления часто бывают нетривиальными.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ