Подтвердить что ты не робот

Диаграмма DAO - где заключаются транзакции?

Итак, у меня есть эта общая вещь DAO, и по номиналу мне кажется, что все в порядке. Это в основном смоделировано после примера приложения CaveatEmptor от парней Hibernate.

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

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

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

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

4b9b3361

Ответ 1

Я помню, что Martin Fowler советует держать контроль над транзакцией в бизнес-уровне, потому что транзакция - деловая проблема. (Если вы создаете класс 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;
}

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

Затем этот класс работает с сеансом nHibernate для начала и фиксации транзакций.

Ответ 5

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

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

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 или если это возможно).