Чи слід розміщувати @Transactional
в DAO
класах та / або їх методах чи краще анотувати класи сервісу, які викликають за допомогою об’єктів DAO? Або має сенс коментувати обидва "шари"?
Чи слід розміщувати @Transactional
в DAO
класах та / або їх методах чи краще анотувати класи сервісу, які викликають за допомогою об’єктів DAO? Або має сенс коментувати обидва "шари"?
Відповіді:
Я думаю, що транзакції належать на рівні Сервісу. Це той, хто знає про одиниці роботи та випадки використання. Це правильна відповідь, якщо у Службі введено декілька DAO, які потребують спільної роботи за одну транзакцію.
Загалом я погоджуюся з іншими, що стверджують, що транзакції зазвичай починаються на рівні сервісу (залежно від деталізації, яка вам потрібна, звичайно).
Однак у той же час я також почав додавати @Transactional(propagation = Propagation.MANDATORY)
до свого шару DAO (та інших шарів, яким заборонено починати транзакції, але потрібні існуючі), оскільки набагато простіше виявити помилки, коли ви забули розпочати транзакцію у виклику ( наприклад послуга). Якщо ваш DAO зазначається з обов'язковим розповсюдженням, ви отримаєте виняток, який стверджує, що немає активної транзакції, коли використовується метод.
У мене також є тест на інтеграцію, коли я перевіряю всі боби (процесор пост-процесора) на цю анотацію і не вдається, якщо є @Transactional
анотація з поширенням, крім обов'язкового, в квасолі, яка не належить до рівня послуг. Таким чином я переконуюсь, що ми не починаємо транзакції на неправильному шарі.
@Transactional
клас реалізації сервісу, і я повинен перейти @Transactional(propagation = MANDATORY)
на реалізацію класу DAO (репозиторій)?
Анотації щодо транзакцій слід розміщувати навколо всіх операцій, які нероздільні.
Наприклад, ваш дзвінок - "змінити пароль". Це складається з двох операцій
Отже, у вищесказаному, якщо аудит не вдасться, то чи повинна змінити пароль також невдало? Якщо так, то транзакція повинна бути приблизно 1 і 2 (так на рівні обслуговування). Якщо електронна пошта виходить з ладу (ймовірно, має бути якась безпечна помилка щодо цього, щоб вона не вийшла з ладу), тоді вона повинна повернути пароль зміни та аудит?
Ось такі питання, які вам потрібно задавати, вирішуючи, куди їх поставити @Transactional
.
Правильна відповідь для традиційних архітектур весни - розмістити транзакційну семантику на класах обслуговування з причин, які інші вже описали.
Навесні тенденція навесні спрямована на доменний дизайн (DDD). Весна Роо чудово демонструє тенденцію. Ідея полягає в тому, щоб зробити об'єкти домену 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
inJava
Зазвичай слід розмістити транзакцію на рівні обслуговування.
Але, як було сказано раніше, атомність операції - це те, що нам підказує, де необхідна анотація. Таким чином, якщо ви використовуєте рамки типу 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);
}
Весняний документ: 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 на рівні обслуговування. Хоча ви можете зловити в контролері, але контролер повинен бути максимально чистим.
Якщо ви надсилаєте пошту чи sms окремим потоком після завершення всіх параметрів збереження, видалення та оновлення, ви можете це зробити в сервісі після завершення транзакції на вашому середньому рівні. Знову ж таки, якщо ви згадуєте @Transactional на рівні обслуговування, пошта буде відправлятися, навіть якщо транзакція не вдасться.
Отже, наявність середнього шару @Transaction допоможе зробити ваш код кращим та легшим у роботі. В іншому випадку, якщо ви використовуєте в шарі DAO, ви не зможете відмовити всі операції. Якщо ви використовуєте в рівні сервісу, можливо, вам доведеться використовувати AOP (орієнтоване на аспекти програмування) в певних випадках.
В ідеалі сервісний рівень (менеджер) представляє вашу логіку бізнесу, а отже, слід @Transactional
зазначити, що рівень. Сервісний шар може викликати різні DAO для виконання операцій з DB. Давайте припустимо ситуації, коли у вас є N кількість операцій DAO в сервісному методі. Якщо ваша перша операція DAO не вдалася, інші можуть все-таки пройти, і ви виявите невідповідний стан БД. Шар службового коментування може врятувати вас від таких ситуацій.
Я вважаю за краще використовувати @Transactional
на рівні послуг на рівні методу.