Если вы поместите @Transactional
в DAO
классах и / или их методы или лучше аннотировать классы услуг , которые призывают с помощью объектов DAO? Или имеет смысл аннотировать оба «слоя»?
Если вы поместите @Transactional
в DAO
классах и / или их методы или лучше аннотировать классы услуг , которые призывают с помощью объектов DAO? Или имеет смысл аннотировать оба «слоя»?
Ответы:
Я думаю, что транзакции относятся к уровню сервиса. Это тот, который знает о единицах работы и случаях использования. Это правильный ответ, если у вас есть несколько DAO, введенных в Сервис, которые должны работать вместе в одной транзакции.
В целом я согласен с другими, утверждая, что транзакции обычно начинаются на уровне обслуживания (конечно, в зависимости от степени детализации, которая вам требуется).
Тем не менее, в то же время я также начал добавлять @Transactional(propagation = Propagation.MANDATORY)
к своему уровню DAO (и другим уровням, которым не разрешено запускать транзакции, но требуются существующие), потому что намного легче обнаружить ошибки, когда вы забыли начать транзакцию в вызывающей стороне ( например, услуга). Если ваш DAO аннотирован обязательным распространением, вы получите исключение, указывающее, что при вызове метода нет активной транзакции.
У меня также есть интеграционный тест, в котором я проверяю все bean-компоненты (постпроцессор bean-компонентов) на наличие этой аннотации и завершаю работу, если есть @Transactional
аннотация с распространением, отличным от Mandatory, в bean-компоненте, который не принадлежит уровню сервисов. Таким образом я убеждаюсь, что мы не запускаем транзакции не на том слое.
@Transactional
класс реализации Service, и я должен включить реализацию класса @Transactional(propagation = MANDATORY)
DAO (хранилище)?
Транзакционные аннотации должны размещаться вокруг всех операций, которые неотделимы.
Например, ваш звонок «сменить пароль». Это состоит из двух операций
Таким образом, в вышеприведенном случае, если проверка завершится неудачно, то также может произойти сбой смены пароля? Если это так, то транзакция должна быть около 1 и 2 (так на уровне обслуживания). Если электронная почта не работает (вероятно, на ней должна быть какая-то защита от сбоев, чтобы она не сработала), то должна ли она отменить изменение пароля и аудит?
Это те вопросы, которые вам нужно задавать при принятии решения о том, где поставить @Transactional
.
Правильный ответ для традиционных архитектур Spring заключается в размещении транзакционной семантики в классах обслуживания по причинам, которые уже были описаны другими.
Весной появляется тенденция к разработке на основе доменов (DDD). Spring Roo прекрасно иллюстрирует эту тенденцию. Идея состоит в том, чтобы сделать POJO объекта домена намного богаче, чем на типичных архитектурах Spring (обычно они анемичны ), и, в частности, поместить семантику транзакций и персистентности в сами объекты домена. В случаях, когда все, что требуется, это простые операции CRUD, веб-контроллеры работают непосредственно с объектами POJO домена (в этом контексте они функционируют как объекты), и уровень обслуживания отсутствует. В случаях, когда требуется некоторая координация между объектами домена, у вас может быть обработчик служебного компонента, который@Transaction
по традиции. Вы можете установить распространение транзакции на объектах домена примерно REQUIRED
так, чтобы объекты домена использовали любые существующие транзакции, например транзакции, которые были запущены в компоненте службы.
Технически эта техника использует AspectJ и <context:spring-configured />
. Roo использует определения между типами AspectJ, чтобы отделить семантику сущности (транзакции и постоянство) от содержимого объекта домена (в основном, полей и бизнес-методов).
Обычным случаем будет аннотирование на уровне сервисного уровня, но это действительно зависит от ваших требований.
Аннотирование на сервисном уровне приведет к более длительным транзакциям, чем аннотирование на уровне DAO. В зависимости от уровня изоляции транзакции, который может вызвать проблемы, так как параллельные транзакции не будут видеть изменения друг друга, например. ПОВТОРЯЕМЫЙ ЧИТАТЬ.
Аннотирование в DAO сделает транзакции настолько короткими, насколько это возможно, с тем недостатком, что функциональность, предоставляемая вашим сервисным уровнем, не будет выполняться в одной транзакции (с возможностью отката).
Не имеет смысла аннотировать оба слоя, если режим распространения установлен по умолчанию.
Я помещаю @Transactional
на @Service
слой и устанавливаю rollbackFor
любое исключение и readOnly
оптимизирую транзакцию дальше.
По умолчанию @Transactional
будет выполняться поиск только « RuntimeException
Непроверенных исключений», а при установке отката на « Exception.class
Проверенные исключения» будет выполняться откат для любого исключения.
@Transactional(readOnly = false, rollbackFor = Exception.class)
Или имеет смысл аннотировать оба «слоя»? - не имеет ли смысла аннотировать и уровень обслуживания, и уровень дао - если кто-то хочет удостовериться, что метод DAO всегда вызывается (распространяется) из уровня обслуживания с распространением "обязательно" в DAO. Это обеспечило бы некоторое ограничение для вызова методов DAO из уровня пользовательского интерфейса (или контроллеров). Кроме того, при модульном тестировании уровня DAO, в частности, аннотирование DAO также гарантирует его тестирование на функциональность транзакций.
propagation=Propagation.REQUIRES_NEW
. В противном случае, в большинстве случаев, включая распространение = обязательное, DAO будет просто участвовать в существующей транзакции, запущенной уровнем обслуживания.
Кроме того, Spring рекомендует использовать аннотацию только для конкретных классов, а не для интерфейсов.
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Для транзакции на уровне базы данных
в основном я использовал @Transactional
в DAO только на уровне метода, поэтому конфигурация может быть специально для метода / с использованием по умолчанию (обязательно)
Метод DAO, который получает выборку данных (выберите ..) - это не нужно,
@Transactional
это может привести к некоторым издержкам из-за перехватчика транзакции / и прокси-сервера AOP, которые также должны быть выполнены.
Методы DAO, которые делают вставку / обновление, получат @Transactional
очень хороший блог о трансакционном
Для уровня приложения -
я использую транзакционную для бизнес-логики, я хотел бы иметь возможность откат в случае непредвиденной ошибки
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
Transactional
вJava
Обычно нужно поместить транзакцию на уровень сервиса.
Но, как было сказано ранее, атомарность операции - это то, что говорит нам, где необходима аннотация. Таким образом, если вы используете фреймворки, такие как Hibernate, где одна операция «сохранить / обновить / удалить / ... модификацию» над объектом может потенциально изменить несколько строк в нескольких таблицах (из-за каскада через граф объектов) Конечно, также должно быть управление транзакциями по этому конкретному методу DAO.
@Transactional
Аннотации должны быть размещены вокруг всех операций, которые неразделимы. Использование @Transactional
распространения транзакции обрабатывается автоматически. В этом случае, если текущий метод вызывает другой метод, этот метод будет иметь возможность присоединиться к текущей транзакции.
Итак, давайте возьмем пример:
У нас есть 2 модели то есть Country
и City
. Реляционное сопоставление Country
и City
модель похожи на то, что Country
можно иметь несколько городов, поэтому сопоставление похоже на
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
Здесь Страна сопоставлена с несколькими городами с получением их Lazily
. Таким образом, здесь возникает роль, @Transactinal
когда мы получаем объект Country из базы данных, тогда мы получим все данные объекта Country, но не получим набор городов, потому что мы выбираем города LAZILY
.
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
Когда мы хотим получить доступ к множеству городов из объекта страны, тогда мы получим нулевые значения в этом наборе, потому что объект набора, созданный только в этом наборе, не инициализируется с этими данными, чтобы получить значения набора, который мы используем, @Transactional
т.е.
//with @Transactional
@Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
Object object = country.getCities().size();
}
Таким образом, в основном @Transactional
Сервис может сделать несколько вызовов в одной транзакции, не закрывая соединение с конечной точкой.
@Transactional
самом деле
@Transactional
Следует использовать на уровне услуг , поскольку он содержит бизнес - логику. Уровень DAO обычно имеет только операции CRUD базы данных.
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
Spring doc: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
Сервисный слой - лучшее место для добавления @Transactional
аннотаций, так как большая часть бизнес-логики, представленной здесь, содержит поведение на уровне детализации.
Предположим, что мы добавляем его в DAO, и из службы мы вызываем 2 класса DAO, один из которых был неудачным, а другой - успешным, в этом случае, если @Transactional
не в службе, одна БД выполнит фиксацию, а другая откатится.
Поэтому я рекомендую использовать эту аннотацию с умом и использовать только на уровне сервиса.
Прежде всего давайте определимся, где мы должны использовать транзакцию ?
Я думаю, что правильный ответ - когда нам нужно убедиться, что последовательность действий будет завершена вместе как одна атомарная операция, или никакие изменения не будут сделаны, даже если одно из действий не выполнится.
Хорошо известна практика внедрения бизнес-логики в сервисы. Поэтому сервисные методы могут содержать разные действия, которые должны выполняться как единая логическая единица работы. Если так - то такой метод должен быть помечен как транзакционный . Конечно, не каждый метод требует такого ограничения, поэтому вам не нужно помечать весь сервис как транзакционный .
И даже больше - не забудьте учесть, что @Transactional, очевидно, может снизить производительность метода. Чтобы увидеть всю картину, вы должны знать уровни изоляции транзакций. Знание этого может помочь вам избежать использования @Transactional там, где это необязательно.
Лучше держать @Transactional на отдельном промежуточном уровне между DAO и Service Layer. Поскольку откат очень важен, вы можете поместить все ваши манипуляции с БД на средний уровень и написать бизнес-логику на уровне обслуживания. Средний слой будет взаимодействовать с вашими слоями DAO.
Это поможет вам во многих ситуациях, таких как ObjectOptimisticLockingFailureException - это исключение возникает только после завершения вашей транзакции. Таким образом, вы не можете поймать его в среднем слое, но теперь вы можете поймать его в слое сервиса. Это было бы невозможно, если у вас есть @Transactional в слое Service. Хотя вы можете поймать контроллер, но контроллер должен быть как можно более чистым.
Если вы отправляете почту или смс в отдельном потоке после выполнения всех параметров сохранения, удаления и обновления, вы можете сделать это в сервисе после завершения транзакции на среднем уровне. Опять же, если вы упомянули @Transactional на уровне сервиса, ваша почта будет отправляться, даже если ваша транзакция не удалась.
Таким образом, наличие среднего слоя @Transaction поможет сделать ваш код лучше и проще в обращении. В противном случае, если вы используете слой DAO, вы не сможете откатить все операции. Если вы используете сервисный уровень, вам, возможно, придется использовать AOP (Aspect Oriented Programming) в некоторых случаях.
В идеале сервисный уровень (менеджер) представляет вашу бизнес-логику, и поэтому он должен быть аннотирован с помощью @Transactional
.Service layer может вызывать разные DAO для выполнения операций с БД. Предположим, что у вас есть N операций DAO в сервисном методе. Если ваша 1-я операция DAO завершилась неудачно, другие могут все еще быть пропущены, и вы получите несовместимое состояние БД. Слой Annoating Service может спасти вас от таких ситуаций.
Я предпочитаю использовать @Transactional
на уровне услуг на уровне метода.