Где находится аннотация @Transactional?


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

19 449

19 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

весной наметилась тенденция к домен-управляемая конструкция (DDD). Spring Roo прекрасно иллюстрирует тенденцию. Идея состоит в том, чтобы сделать объект домена 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 в частности-наличие аннотированного DAO также гарантирует, что он будет протестирован транзакционная функциональность.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

у нас есть 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 
}

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

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

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

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

Github проект-java-algos

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

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

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

enter image description here

The @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 и уровнем сервиса. Поскольку откат очень важен, вы можете разместить все свои манипуляции с БД на среднем уровне и написать бизнес-логику на уровне сервиса. Средний слой будет взаимодействовать с вашими слоями DAO.

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

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

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