Q: Экземпляр UnitOfWork
ограничен сессией?
В главе 11 рассматриваемой книги Мартина вы читаете:
«Единица работы отслеживает все, что вы делаете во время бизнес-транзакции, которая может повлиять на базу данных. Когда вы закончите, он выяснит все, что нужно сделать, чтобы изменить базу данных в результате вашей работы. [...]
«Как только вы начинаете делать что-то, что может повлиять на базу данных, вы создаете Единицу работы, чтобы отслеживать изменения. Каждый раз, когда вы создаете, изменяете или удаляете объект, вы сообщаете Единицу работы. Вы также можете сообщить ему об объектах, которые вы прочитали, чтобы он мог проверять наличие непоследовательных чтений, проверяя, что ни один из объектов не изменился в базе данных во время бизнес-транзакции. ”
Итак, UnitOfWork
не должен быть привязан к сеансу. Степень существования экземпляра UnitOfWork
зависит от вашего дизайна. В примере Мартина из той же книги мы видим, что экземпляр UnitOfOWork создается для каждого HTTP-запроса (который, я считаю, является наиболее классическим использованием).
class UnitOfWorkServlet...
final protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
UnitOfWork.newCurrent();
handleGet(request, response);
UnitOfWork.getCurrent().commit();
} finally {
UnitOfWork.setCurrent(null);
}
}
abstract void handleGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
В спецификации JPA единица работы можетмогут быть двух разных типов: контекст персистентности в области транзакций, контекст расширенной персистентности, но можно также обработать его вручную и получить управляемую приложением единицу работы ( см. этот другой ответ ). Какой вы используете, зависит от варианта использования.
В: Сколько экземпляров IdentityMap нам нужно?
Мартин Фаулер также отвечает на этот вопрос в книге. В его разделе IdentityMap
есть целый раздел об этом.
Книга гласит:
«Здесь решение варьируется между одной картой на класс и одной картой для всей сессии. "
Где вы могли бы понимать сессию как UnitOfWork
в этом случае. Далее в книге он объясняет:
« Если у вас несколько карт,очевидный маршрут - одна карта для каждого класса или таблицы, которая хорошо работает, если схема базы данных и объектные модели совпадают ».
Несколько абзацев позже Мартин объясняет, куда поместить IdentityMap
:
«Карты удостоверений должны быть где-то там, где их легко найти. Они также привязаны к контексту процесса, в котором вы работаете. Вы должны убедиться, что каждый сеанс получает свой собственный экземпляр, которыйизолированы от любого другого экземпляра сеанса. Таким образом, вам нужно поместить Identity Map в объект, специфичный для сеанса. Если вы используете Unit of Work, это, безусловно, лучшее место для Identity Maps, так как Unit of Work - это главное место для отслеживания данных, поступающих в базу данных или из нее. Если у вас нет Единицы работы, лучше всего подойдет Реестр, привязанный к сеансу ».
Итак, у вас есть, если ваш UnitOfWork
связан с запросомтогда у вас будет один или несколько IdentityMaps
в этом экземпляре вашего UnitOfWork
.
Итак, ограничена ли единица работы бизнес-операцией?
Да, это так.
Теперь степень «бизнес-транзакции» может быть недолгой, например, привязанной к сроку действия HTTP-запроса, как в приведенном выше примере Мартина Фаулера, где UnitOfWork, по-видимому, привязан к локальной переменной потоказапрос.
Или «бизнес-транзакция» может охватывать несколько запросов, например, в JPA существует концепция расширенного контекста постоянства, где постоянный контекст в JPA соответствует шаблону UnitOfWork
. В расширенном контексте постоянства бизнес-транзакция продлевается на более длительный период времени. Классическим примером этого является корзина покупок: когда пользователь начинает помещать товары в свою корзину покупок, создается контекст / единица работы, и контекст не очищается, пока пользователь не извлечет свою корзину покупок (фиксируя изменения)или ее сеанс истекает (отбрасывая все в единице работы).
Теперь также существует идеяКонтекст, управляемый приложениями, который в основном означает, запускайте вашу единицу работы, когда вы считаете, что это уместно, и закрывайте ее, когда она вам больше не нужна. Например, предположим, что у вас есть настольное приложение и небольшая база данных с изолированным параллелизмом (то есть каждый пользователь управляет только своими собственными данными и никогда не конфликтует с данными других пользователей). Предположим, что данные пользователя могут идеально поместиться в памяти компьютера. В таком случае вы можете запустить UnitOfWork
в начале приложения и использовать его как своего рода кеш для вашей базы данных. Когда это уместно, вы можете записать UnitOfWork на диск, например, когда пользователь нажимает кнопку сохранения, но все еще сохраняет его активным. Когда пользователь закрывает приложение, UnitOfWork отбрасывается.
Итак, вы можете видеть много нюансов о том, что на самом деле означает «бизнес-операция», или о том, как долго должна существовать UnitOfWork
.
Несколько единиц работы
Исходя из приведенного выше объяснения, вы можете иметь несколько единиц работы, среди прочего, по следующим причинам:
- Один
UnitWork
на запрос, и если ваше приложение обрабатывает параллельные запросы, то у вас будет одновременно несколько единиц работы. - Один
UnitOfWork
на расширенную транзакцию и т. Д.,привязан к сеансу пользователя. Если у вас несколько пользователей, у вас может быть несколько единиц работы.
Однако, кроме них, я нашел и другие причины, по которым вы можете захотеть создать новую единицу работы во время одной и той же бизнес-транзакции. ".
Нередко во время выполнения бизнес-транзакции вы можете захотеть выполнить еще одну полностью независимую транзакцию. Например, предположим, что для того, чтобы разместить заказ для клиента, запись о клиенте должна существовать в базе данных, но если запись о клиенте не может быть создана (например, возможно, из-за того, что другой клиент имеет тот же конфликтующий адрес электронной почты), вы все равно хотите разместитьзаказ находится в состоянии ожидающего рассмотрения.
Итак, проблема, с которой вы здесь сталкиваетесь, заключается в том, что если вы попытаетесь создать клиента во время бизнес-операции размещения заказа и неудачного создания клиента, это загрязнит вашу транзакцию заказа и вашединица работы вынуждена откатывать все. В такой ситуации вы можете создать новую единицу работы и, следовательно, новую отдельную транзакцию базы данных, чтобы создать клиента, и если эта отдельная единица работы не сможет создать клиента, это не загрязнит вашу единицу создания заказа. работать, позволяя вам принять меры для сохранения вашего заказа без участия клиента в состоянии ожидающего рассмотрения.
Я считаю, что концепция JPA «контекст» является очень хорошим примером того, как определить единицу работы. Я бы посоветовал вам изучить их спецификацию , в частности их раздел о EntityManager
.