Де належить анотація @Transactional?


528

Чи слід розміщувати @Transactionalв DAOкласах та / або їх методах чи краще анотувати класи сервісу, які викликають за допомогою об’єктів DAO? Або має сенс коментувати обидва "шари"?

Відповіді:


537

Я думаю, що транзакції належать на рівні Сервісу. Це той, хто знає про одиниці роботи та випадки використання. Це правильна відповідь, якщо у Службі введено декілька DAO, які потребують спільної роботи за одну транзакцію.


Я з цим згоден. Іноді це не має значення, але іноді ви можете скористатися цим, наприклад, сплячий сеанс перебуває на певній транзакції, тому всі завантажені об'єкти знаходяться в кеші першого рівня, і вам не потрібно повторно приєднувати об'єкти до сеансу, плюс ліниво завантажений властивості функціонують без пуху.
dma_k

5
Чи може глобальна транзакція складатися з декількох таких @Transactional (поширення = розповсюдження.REQUIRED)? Або @Transactional - це завжди межа для транзакції? Я не впевнений, що я отримав це з документації, але здається, що ви можете створити навіть транзакції, що складаються з @Transactional методів і всього, що працює всередині
lisak

1
Так, я думаю, що найповніший @Transactional - це той, що стає межею для транзакції, якщо ввімкнено Propagation.REQUIRED.
duffymo


309

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

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

У мене також є тест на інтеграцію, коли я перевіряю всі боби (процесор пост-процесора) на цю анотацію і не вдається, якщо є @Transactionalанотація з поширенням, крім обов'язкового, в квасолі, яка не належить до рівня послуг. Таким чином я переконуюсь, що ми не починаємо транзакції на неправильному шарі.


3
Якщо це потрібно зробити в шарі дао (інтерфейси), в дао імпл. шар або обидва?
Йохан

3
Я не знаю, чи вписується вона в цю дискусію, але ще одна порада може бути додана @Transactional (readOnly = true) в dao impl в операціях, що не пишуть.
maxivis

10
@Johan Spring радить розміщувати анотації з транзакцій на класах реалізації замість інтерфейсів.
Матіас Г.

Мені дуже подобається ця ідея, намагаючись це зробити і для моїх проектів
Томас Ейнваллер,

1
Перегляньте, чи я розумію ... Ви хочете сказати, що я повинен ставити @Transactionalклас реалізації сервісу, і я повинен перейти @Transactional(propagation = MANDATORY)на реалізацію класу DAO (репозиторій)?
Джон Генкель

93

Анотації щодо транзакцій слід розміщувати навколо всіх операцій, які нероздільні.

Наприклад, ваш дзвінок - "змінити пароль". Це складається з двох операцій

  1. Змініть пароль.
  2. Аудит змін.
  3. Надішліть клієнту електронну пошту, що пароль змінився.

Отже, у вищесказаному, якщо аудит не вдасться, то чи повинна змінити пароль також невдало? Якщо так, то транзакція повинна бути приблизно 1 і 2 (так на рівні обслуговування). Якщо електронна пошта виходить з ладу (ймовірно, має бути якась безпечна помилка щодо цього, щоб вона не вийшла з ладу), тоді вона повинна повернути пароль зміни та аудит?

Ось такі питання, які вам потрібно задавати, вирішуючи, куди їх поставити @Transactional.


41

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

Навесні тенденція навесні спрямована на доменний дизайн (DDD). Весна Роо чудово демонструє тенденцію. Ідея полягає в тому, щоб зробити об'єкти домену POJO набагато багатшими, ніж вони є в типових архітектурах Spring (зазвичай вони анемічні ), і, зокрема, розмістити семантику транзакцій та постійності на самих об'єктах домену. У випадках, коли все, що потрібно, - це прості CRUD-операції, веб-контролери працюють безпосередньо над POJO-об'єктами домену (вони функціонують як сутності в цьому контексті), і немає рівня обслуговування. У випадках, коли між об’єктами домену потрібна якась координація, ви можете мати службовий інструмент для обробки@Transactionяк за традицією. Ви можете встановити розповсюдження транзакцій на об’єктах домену на щось подібне, REQUIREDщоб об’єкти домену використовували будь-які існуючі транзакції, наприклад транзакції, розпочаті на сервісному бобі.

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


38

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

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

Анотація на DAO дозволить зробити транзакції максимально короткими, з тим недоліком, що функціональність, на яку виявляється ваш рівень обслуговування, не буде виконана в рамках однієї трансляції (яка може бути відкатана).

Немає сенсу коментувати обидва шари, якщо режим поширення встановлено за замовчуванням.


31

Я розміщую @Transactionalна @Serviceшарі і встановлюю rollbackForбудь-які винятки та readOnlyдодатково оптимізую транзакцію.

За замовчуванням @Transactionalбуде шукати лише RuntimeException(Без Exception.classперевірених винятків) , встановивши відкат на (Перевірені винятки), він відкатується за будь-яким винятком.

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

Див. Перевірені та неперевірені виключення .


17

Або має сенс коментувати обидва "шари"? - чи не має сенсу анотувати як службовий рівень, так і шар дао, - якщо потрібно переконатися, що метод DAO завжди викликається (розповсюджується) з сервісного рівня з поширенням "обов'язкового" в DAO. Це дало б певне обмеження для виклику методів DAO із рівня користувальницького інтерфейсу (або контролерів). Крім того, - зокрема, при тестуванні одиничного шару DAO - анотація DAO також забезпечить тестування функціональності транзакцій.


Не вдалося б зробити це результатом вкладених транзакцій? І всі найтонші проблеми, які пов'язані з цим?
HDave

11
Ні, в JPA немає вкладених транзакцій. Зміщення їх в обох було б чудово - якщо ви вже уклали угоду, коли потрапили в DAO, ця транзакція буде продовжена.
crazy4jesus

4
Вкладені транзакції можуть статися, якщо ви використовуєте propagation=Propagation.REQUIRES_NEW. Інакше для більшості випадків, включаючи пропонування = обов'язкове, DAO просто братиме участь у існуючій транзакції, розпочаті сервісним рівнем.
дан Картер


15

Для транзакцій на рівні бази даних

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

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

  2. Методи DAO, які вставляють / оновлюють, отримають @Transactional

дуже хороший блог про транскрипцію

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

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

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

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

6
+1 дуже приємна стаття про TransactionalinJava
дуб

11

Зазвичай слід розмістити транзакцію на рівні обслуговування.

Але, як було сказано раніше, атомність операції - це те, що нам підказує, де необхідна анотація. Таким чином, якщо ви використовуєте рамки типу Hibernate, де одна операція "збереження / оновлення / видалення / ... модифікація" на об'єкті може змінити декілька рядків у кількох таблицях (через каскад через графік об'єкта), Звичайно, має бути також управління транзакціями за цим конкретним методом DAO.


10

@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Сервіс може здійснювати багаторазовий дзвінок за одну транзакцію, не закриваючи з'єднання з кінцевою точкою.


2
дуже інформативно, дякую! Тільки те, що я шукав, пояснення того, що @Transactionalнасправді є
CybeX

6

Краще мати його в сервісному шарі! Це чітко пояснено в одній зі статей, на які я зіткнувся вчора! Ось посилання, яке ви можете перевірити!


5

введіть тут опис зображення

Це @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);
}

Весняний документ: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html


5

Службовий рівень найкраще додати @Transactionalпримітки, оскільки більшість ділових логік тут присутня, вона містить поведінку на рівні деталей використання у випадку використання.

Припустимо, ми додамо його до DAO, і з сервісу ми викликаємо 2 класи DAO, один невдалий та інший успіх; у цьому випадку, якщо @Transactionalнемає на сервісі, одна БД почне вчиняти, а інша відкине.

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

Github-проект - java-algos


Винятки, такі як ObjectOptimisticLockingFailureException, відбуваються лише після завершення транзакції. Якщо у вас є окремі потоки для інших операцій, таких як поштова служба, ця конструкція повністю не працює. Зараз ми страждаємо. Тільки лівим рішенням буде АОП.
Набін Кумар Хатівада

4

Перш за все давайте визначимось, де нам потрібно використовувати транзакції ?

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

Загальновідома практика застосовувати бізнес-логіку в послугах. Тож методи обслуговування можуть містити різні дії, які необхідно виконувати як єдину логічну одиницю роботи. Якщо так - то такий метод повинен бути позначений як транзакційний . Звичайно, не кожен метод вимагає такого обмеження, тому не потрібно маркувати всю службу як транзакційну .

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


3

Краще зберегти @Transactional в окремому середньому шарі між DAO та службовим шаром. Оскільки відкат є дуже важливим, ви можете розмістити всі ваші маніпуляції з БД в середньому шарі і записати ділову логіку в Service Layer. Середній шар взаємодіє з вашими шарами DAO.

Це допоможе вам у багатьох ситуаціях, таких як ObjectOptimisticLockingFailureException - Цей виняток виникає лише після закінчення транзакції. Отже, ви не можете зафіксувати його в середньому шарі, але зараз ви можете вловити його в сервісному шарі. Це неможливо, якщо у вас є @Transactional на рівні обслуговування. Хоча ви можете зловити в контролері, але контролер повинен бути максимально чистим.

Якщо ви надсилаєте пошту чи sms окремим потоком після завершення всіх параметрів збереження, видалення та оновлення, ви можете це зробити в сервісі після завершення транзакції на вашому середньому рівні. Знову ж таки, якщо ви згадуєте @Transactional на рівні обслуговування, пошта буде відправлятися, навіть якщо транзакція не вдасться.

Отже, наявність середнього шару @Transaction допоможе зробити ваш код кращим та легшим у роботі. В іншому випадку, якщо ви використовуєте в шарі DAO, ви не зможете відмовити всі операції. Якщо ви використовуєте в рівні сервісу, можливо, вам доведеться використовувати AOP (орієнтоване на аспекти програмування) в певних випадках.


2

В ідеалі сервісний рівень (менеджер) представляє вашу логіку бізнесу, а отже, слід @Transactionalзазначити, що рівень. Сервісний шар може викликати різні DAO для виконання операцій з DB. Давайте припустимо ситуації, коли у вас є N кількість операцій DAO в сервісному методі. Якщо ваша перша операція DAO не вдалася, інші можуть все-таки пройти, і ви виявите невідповідний стан БД. Шар службового коментування може врятувати вас від таких ситуацій.



1

@Transactionalвикористовує в службовому рівні, який викликається за допомогою контрольного рівня ( @Controller) та виклику службового рівня на рівень DAO ( @Repository), тобто операції, пов'язані з базою даних.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.