Где принадлежит аннотация @Transactional?

Если вы поместите @Transactional на DAO классы и / или их методы или лучше аннотировать классы служб, которые вызывают с помощью объектов DAO? Или имеет смысл аннотировать оба "слоя"?

19 ответов


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


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

однако, в то же время я также начал добавлять @Transactional(propagation = Propagation.MANDATORY) к моему слою DAO (и другим слоям, которые не разрешены для запуска транзакций, но требуют существующих), потому что гораздо проще обнаружить ошибки, когда вы забыли запустить транзакцию в вызывающем абоненте (например, сервис). Если ваш DAO аннотирован при обязательном распространении вы получите исключение, указывающее, что при вызове метода нет активной транзакции.

у меня также есть интеграционный тест, где я проверяю все бобы (бобовый почтовый процессор) для этой аннотации и терплю неудачу, если есть @Transactional аннотация с распространением, отличным от обязательного, в Бобе, который не принадлежит слою сервисов. Таким образом, я убеждаюсь, что мы не начинаем транзакции на неправильном уровне.


транзакционные аннотации должны быть размещены вокруг всех операций, которые неразделимы.

например, ваш вызов - "изменить пароль". То есть состоит из двух операций

  1. изменить пароль.
  2. аудит изменения.
  3. напишите клиенту, что пароль изменился.

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

Это такие вопросы, которые вам нужно задавать при принятии решения, куда поместить @Transactional.


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

аннотирование на уровне сервиса приведет к более длительным транзакциям, чем аннотирование на уровне DAO. В зависимости от уровня изоляции транзакций, который может создавать проблемы, поскольку параллельные транзакции не будут видеть изменения друг друга, например. ПОВТОРЯЕМОЕ ЧТЕНИЕ.

аннотирование на DAOs будет держать транзакции как можно короче, с недостатком, что функциональность слое служба разоблачения не будет сделано в одной транзакции (rollbackable).

нет смысла комментировать оба слоя, если режим распространения установлен по умолчанию.


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

наметившаяся весной тенденция к домен-управляемая конструкция (DDD). Весна РОО прекрасно иллюстрирует тенденцию. Идея состоит в том, чтобы сделать объект домена POJOs много богаче чем они на типичных архитектурах весны (обычно они анемия), и, в частности, поставить семантику транзакции и персистентности на самих объектах домена. В случаях, когда все, что нужно, - это простые операции CRUD, веб-контроллеры работают непосредственно на POJOs объекта домена (они функционируют как сущности в этом контексте), и нет уровня обслуживания. В случаях, когда требуется какая-то координация между объектами домена, вы можете иметь дескриптор компонента службы, который с @Transaction по традиции. Вы можете установить распространение транзакций на домен возражает против чего-то вроде REQUIRED чтобы объекты домена использовали любые существующие транзакции,такие как транзакции, которые были запущены в служебном компоненте.

Технически этот метод использует AspectJ и <context:spring-configured />. Roo использует определения межтипов AspectJ для отделения семантики сущностей (транзакций и сохраняемости) от материала объекта домена (в основном полей и бизнес-методов).


I место @Transactional на @Service слой и установите для параметра rollbackFor и readOnly для дальнейшей оптимизации сделки.

по умолчанию @Transactional только искать RuntimeException (непроверенные исключения), установив откат в Exception.class (проверенные исключения) он будет откат для любого исключения.

@Transactional(readOnly = false, rollbackFor = Exception.class)

посмотреть проверено против непроверенных исключений.


или имеет смысл аннотировать оба "слоя"? - не имеет смысла аннотировать как слой сервиса, так и слой dao - если нужно убедиться, что метод DAO всегда вызывается (распространяется) из слоя сервиса с распространением "обязательно" в DAO. Это обеспечит некоторое ограничение для методов DAO от вызова из уровня пользовательского интерфейса (или контроллеров). Также - при модульном тестировании слой DAO в частности - имеющих Дао аннотированный также обеспечить ее тестирование на транзакционная функциональность.


кроме того, Spring рекомендует использовать аннотацию только для конкретных классов, а не интерфейсов.

http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html


для транзакции на уровне базы данных

в основном я использовал @Transactional в DAO только на уровне метода, поэтому конфигурация может быть специально для метода / использования default (required)

  1. метод DAO, который получает выборку данных (select .. ) - не нужно @Transactional это может привести к некоторым накладным расходам из-за перехватчик транзакций / и прокси-сервер AOP, которые должны выполняться как что ж.

  2. методы DAO, которые вставляют / обновляют, получат @Transactional

очень хороший блог transctional

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

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}

обычно следует поместить транзакцию на уровне сервиса.

но, как было сказано ранее, атомарность операции-это то, что говорит нам, где необходима аннотация. Таким образом, если вы используете фреймворки, такие как Hibernate, где один "сохранить/обновить/удалить/...операция "модификация" на объекте может изменять несколько строк в нескольких таблицах (из-за каскада через граф объекта), конечно, также должно быть управление транзакциями на этом конкретном DAO метод.


лучше иметь его на уровне сервиса! Это четко объясняется в одной из статей, с которой я столкнулся вчера! Вот ссылке что можно проверить!


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

так давайте возьмем пример:

у нас есть 2 модели, т. е. Country и City. Реляционное отображение Country и City модель как один Country может иметь несколько городов так сопоставление как,

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

здесь Страна отображается на несколько городов с их выборкой Lazily. Так вот приходит роль @Transactinal когда мы извлекаем объект страны из базы данных, мы получим все данные объекта страны, но не получим набор городов, потому что мы получаем города LAZILY.

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

когда мы хотим получить доступ к набору городов из объекта country, мы получим нулевые значения в этом наборе, потому что объект набора, созданный только этот набор, не инициализируйте там данные, чтобы получить значения набора, который мы используем @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 служба is может сделать несколько вызовов в одной транзакции без закрытия соединения с конечной точкой.


прежде всего, давайте определимся, где мы должны использовать сделки?

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

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

и еще - не забудьте принять во внимание, что @Transactional очевидно, может снизить эффективность метода. Чтобы увидеть всю картину, вы должны знать уровни изоляции транзакций. Зная, что может помочь вам избежать использования @Transactional где это не обязательно нужно.


Service layer-лучшее место для добавления @Transactional аннотации как и большая часть бизнес-логики, присутствующей здесь, она содержит поведение прецедента уровня детализации.

Предположим, мы добавляем его в DAO и из службы мы вызываем 2 класса DAO, один неудачный и другой успех , в этом случае, если @Transactional не на службе один БД зафиксирует и другой откатит.

поэтому моя рекомендация-использовать эту аннотацию с умом и использовать только на уровне сервиса.

Github проект-java-algos


В идеале Service layer (Manager) представляет вашу бизнес-логику и, следовательно, она должна быть аннотирована @Transactional.Уровень обслуживания может вызывать различные DAO для выполнения операций БД. Предположим, что в методе службы имеется N операций DAO. Если ваша 1-я операция DAO не удалась, другие могут быть все еще переданы, и вы закончите непоследовательным состоянием БД. Аннотирование уровня сервиса может избавить вас от таких ситуаций.


Я предпочитаю использовать @Transactional на уровне сервисов на уровне метода.


@Transactional использует в слое обслуживания, который вызывается с помощью слоя контроллера (@Controller) и вызов уровня сервиса на уровень DAO (@Repository) Я.электронной базы данных.


enter image description here

на @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);
}

Весна doc : https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html


лучше @Transactional в отдельном среднем слое между DAO и слоем сервиса. Поскольку откат очень важен, вы можете разместить все свои манипуляции с БД на среднем уровне и написать бизнес-логику на уровне сервиса. Средний слой будет взаимодействовать со слоями DAO.

Это поможет вам во многих ситуациях, как ObjectOptimisticLockingFailureException - это исключение возникает только после завершения транзакции. Итак, вы не можете поймайте его в среднем слое, но теперь вы можете поймать его в своем слое обслуживания. Это невозможно, если у вас есть @Transactional на уровне службы. Хотя вы можете поймать контроллер, но контроллер должен быть максимально чистым.

Если вы отправляете почту или sms в отдельном потоке после завершения всех параметров сохранения,удаления и обновления, вы можете сделать это в службе после завершения транзакции в среднем слое. Опять же, если вы упомянули @Transactional в слое сервиса, вы отправите идите, даже если ваша транзакция терпит неудачу.

таким образом, наличие среднего уровня @Transaction поможет сделать ваш код лучше и проще в обращении. Иначе, Если вы используете в слое DAO, вы не сможете откатить все операции. Если вы используете в Service layer, вам может потребоваться использовать AOP(аспектно-ориентированное программирование) в некоторых случаях.