Шаблон DAO - куда вписываются транзакции? - PullRequest
22 голосов
/ 21 апреля 2009

Таким образом, у меня есть эта общая вещь DAO, и по номиналу это, кажется, хорошо. Это в основном смоделировано после примера приложения CaveatEmptor от ребят из Hibernate.

Кроме того, у меня есть бизнес-уровень ... внутренности приложения. Он совершенно не знает о какой-либо конкретной реализации DAO.

Все до этого момента кажется нормальным, пока я не начну думать о транзакциях. Если транзакции оставлены для реализации клиенту, то как в мире я могу поддерживать хорошее разделение между своими уровнями? То есть в данный момент я использую Hibernate, и мне не очень хочется добавлять транзакции, специфичные для hibernate, в код моего бизнес-уровня.

Я мог бы создать простой интерфейс транзакций с методами begin, commit и rollback и передать реализацию своему бизнес-уровню ... но ... я не уверен ...

Итак, вот проблема: можете ли вы порекомендовать мне способ сделать это, не используя слово Spring (или EJB, или любую другую дополнительную платформу)?

Ответы [ 5 ]

12 голосов
/ 21 апреля 2009

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

Вы можете попробовать реализовать TransactionScope, так как в .NET он работает примерно так

using (TransactionScope ts = new TransactionScope())
{
  ...
}

Это то же самое, что и (не совсем, но если вы парень из Java, это более очевидно для вас)

TransactionScope scope = new TransactionScope();
try
{
 ...
 scope.Commit();
}
catch(Exception ex)
{
  scope.Rollback();
  throw;
}

Чтобы отделить бизнес-уровень от любых технологий DAO, вы можете добавить TransactionFactory на языке вашего домена, который возвращает ITransactionScope (интерфейс), который вы определили с помощью методов Commit и Rollback. Таким образом, ваш уровень домена не привязан к уровню DAO, а только конкретная реализация TransactionFactory.

ITransactionScope scope = transactionFactory.CreateTransaction();
try
{
 ...
 scope.Commit();
}
catch(Exception ex)
{
  scope.Rollback();
  throw;
}
6 голосов
/ 21 сентября 2009

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

Независимо от того, какой веб-каркас используется (Struts, JSF, GWT и т. Д.), Обычно существует «шов», в котором можно выполнить разграничение транзакции. В Struts это может быть базовый класс Action. В GWT это может быть базовый класс RemoteServiceImpl.

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

Я широко применил эту стратегию в большом и сложном бизнес-веб-приложении, и она зарекомендовала себя очень хорошо.

3 голосов
/ 10 марта 2011

Может быть, уже слишком поздно для ответа, но как насчет создания другого класса для конкретных транзакций, который находится между бизнес-уровнем и уровнем dao? Например. если методы a () и b () из DAO должны быть запущены в транзакции для какого-то определенного бизнес-метода foo (), то создайте что-то вроде fooInTransaction (), которая запускает транзакцию и вызывает в ней a () и b () , Бизнес-метод foo () делегирует его.

Это позволит сохранить бизнес-код в чистоте и избежать дублирования путем повторного факторинга.

1 голос
/ 21 апреля 2009

В прошлом я помещал логику транзакций в корневой DAO для иерархии DAO, которые соответствуют иерархии объектов в вашей модели, которые представляют единый твердый объект в системе.

Т.е., если у вас есть и X, у которого много Y, и вы хотите хранить и извлекать X и их Y одновременно как один составной объект, тогда ваш DAO для X должен также вызывать DAO для Y. Тогда вы можете поместить транзакцию вокруг всего в ваших методах add () и update () в DAO для X - и даже сделать пакет Y DAO частным, чтобы скрыть его от основной бизнес-логики. Вместо бизнес логики:

XDAO xDAO = new XDAO(conn);
xDAO.startTransaction();
boolean success = xDAO.add(x);
if (success)
    for (Y y : x.getYs()) {
        success = YDAO.add(y);
        if (!success) break;
    }
if (success)
    xDAO.commit();
else
    xDAO.rollback();

Вы бы просто получили:

XDAO xDAO = new XDAO(conn);
xDAO.add(x);

(с логикой успеха / принятия / отката, внутренней для этого DAO)

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

1 голос
/ 21 апреля 2009

Ваше право на то, что приложение является хорошим местом для объединения транзакций, поскольку это позволяет составлять более сложные действия, выполняемые различными службами / менеджерами / или как вы хотите их называть.

Простое решение - определить интерфейс ITransaction и использовать какой-либо тип фабрики или DI, чтобы скрыть фактического разработчика ITransaction от вашего приложения. Я катал свой собственный в .net, используя nHibernate, и, по сути, у меня есть базовый класс, который используют все мои менеджеры (в этом случае менеджер содержит бизнес-логику для логического набора сущностей, таких как Membership, Order, которые могут использовать одно или несколько хранилищ). Мой базовый класс имеет ITransaction BeginTransaction (), который динамически создает тип на основе файла конфигурации.

Затем этот класс работает с сессией nHibernate, чтобы начать и зафиксировать транзакции.

...