Как использовать шаблон UnitOfWork вместе с шаблоном IdentityMap? - PullRequest
4 голосов
/ 04 октября 2019

В книге Мартина Фаулера я прочитал о UnitOfWork и IdentityMap шаблонах.

Автор упомянул, что было бы неплохо поместить identityMap в UnitOfWork. Но как это сделать?

Насколько я понял, IdentityMap ограничен сессией, но автор не упомянул это о UnitOfWork

  1. Is UnitOfWorkэкземпляр ограничен сессией?

  2. Допустим, у нас есть клиент и сущность заказа.

    public clas Client{
    
         List<Order> orders;
        ...
    }
    

и мы получили запрос на обновление информации о клиенте (номер телефона) и добавьте новый заказ:

Сколько экземпляров unitOfWork здесь нужно? для каждой сессии? мы должны отделить экземпляры для клиента и для заказа?

Сколько экземпляров IdentityMap нам нужно здесь? для каждого экземпляра? мы должны отделить экземпляры для клиента и для заказа?

Сколько экземпляров IdentityMap нам нужно для каждого экземпляра unitOfWork?

Что если мы получим 2 одновременных запроса?

Ответы [ 2 ]

2 голосов
/ 16 октября 2019

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.

2 голосов
/ 09 октября 2019

UnitOfWork требует области действия в зависимости от продолжительности бизнес-операции, которой вы должны управлять. Если у вас есть бизнес-операция, которая распространяется на несколько запросов, но которую вы должны рассматривать как одну единицу работы, вы должны обрабатывать экземпляр с согласованной областью действия (например, сеанс).

Единица работыбудет отслеживать все взаимодействия с базой данных и подтверждать их, создавая транзакцию и внося изменения оптимальным способом (в краткосрочной транзакции базы данных).

JPA / Hibernate по умолчанию реализует этот шаблон, поэтому обычно, еслиВы используете их, у вас нет необходимости реализовывать это самостоятельно. JPA / Hibernate также реализует что-то вроде шаблона Identity Map в кэше первого уровня. Он сохраняет карту сущностей в своем кэше первого уровня, избегая необходимости искать в базе данных более одного раза для продолжительности сеанса и единицы работы.

Как сказано в документации по спящему режиму (read me ):

Самым распространенным шаблоном в многопользовательском клиент-серверном приложении является сеанс на запрос. В этой модели запрос от клиента отправляется на сервер, где выполняется постоянный уровень Hibernate. Открывается новый сеанс Hibernate, и все операции с базами данных выполняются в этой единице работы. По завершении работы и после подготовки ответа для клиента сеанс сбрасывается и закрывается. Используйте одну транзакцию базы данных для обслуживания запроса клиентов, запуска и принятия его при открытии и закрытии сеанса. Соотношение между ними однозначно, и эта модель идеально подходит для многих приложений.

Итак, область действия вашей IdentityMap должна быть привязана к вашей бизнес-транзакции и, следовательно, к состоянию. вашего UnitOfWork, который будет отслеживать все изменения во время вашей бизнес-транзакции.

Если ваше приложение обрабатывает короткие транзакции (например, по запросу), это будет довольно просто. Если у вас есть долгосрочная единица работы, охватывающая несколько запросов, ваш unitOfWork должен находиться в области сеанса, а ваша карта удостоверений присоединена к нему.

Это будет ключом к управлению хорошей политикой блокировки с вашей базой данных. в таких случаях, чтобы избежать проблем с параллельными изменениями (объект, считанный в запросе, мог бы быть изменен, когда в таких случаях наступило время фиксации).

Базовый пример реализации шаблона в Java - это (не углубляясь в сферу вопроса): https://github.com/iluwatar/java-design-patterns/tree/master/unit-of-work

...