Java for Beginner
696 subscribers
605 photos
165 videos
12 files
942 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Транзакции в Spring

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

Транзакция — это последовательность операций, которые выполняются как единое целое. Если хотя бы одна из операций в транзакции завершается с ошибкой, все изменения, сделанные в рамках этой транзакции, откатываются.

Ключевые свойства транзакций определяются через ACID:
Atomicity (Атомарность): Все операции в транзакции выполняются или ни одна из них.
Consistency (Согласованность): Транзакция переводит базу данных из одного согласованного состояния в другое.
Isolation (Изолированность): Операции в транзакции не видимы другим транзакциям, пока они не завершатся.
Durability (Устойчивость): После успешного завершения транзакции изменения остаются сохраненными в базе данных, даже в случае сбоя системы.


Пример транзакции в SQL

Предположим, у нас есть две таблицы: accounts и transactions. Нам нужно перевести деньги с одного счета на другой.
BEGIN TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;

INSERT INTO transactions (from_account, to_account, amount) VALUES (1, 2, 100);

COMMIT;
Если любая из этих операций не выполняется, все изменения откатываются.


Транзакции в Spring

Spring обеспечивает управление транзакциями с помощью своих инструментов.

В экосистеме Spring можно использовать различные подходы:
Программное управление транзакциями — требуется вручную управлять началом и завершением транзакций.
Декларативное управление транзакциями — управление транзакциями осуществляется автоматически с помощью аннотаций или конфигураций. Этот подход является наиболее популярным, так как упрощает код.


Программное управление транзакциями

Для ручного управления транзакциями в Spring используется PlatformTransactionManager.

Пример:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Service
public class TransactionalService {

@Autowired
private PlatformTransactionManager transactionManager;

public void performTransactionalOperation() {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);

try {
// Ваши операции с базой данных
performDatabaseOperation1();
performDatabaseOperation2();

// Подтверждение транзакции
transactionManager.commit(status);
} catch (Exception e) {
// Откат транзакции в случае ошибки
transactionManager.rollback(status);
throw e;
}
}

private void performDatabaseOperation1() {
// Код операции 1
}

private void performDatabaseOperation2() {
// Код операции 2
}
}
Этот метод предоставляет полный контроль над транзакциями, но делает код более сложным.


Декларативное управление транзакциями

Более простой и популярный способ — это использование аннотации @Transactional.

Преимущества транзакций:

Целостность данных: Транзакции гарантируют, что данные останутся в корректном состоянии.
Стабильность системы: Операции либо полностью завершаются, либо полностью откатываются.
Упрощение отладки: Вы можете быть уверены, что в случае сбоя данные не будут повреждены.


#Java #Training #Spring #Transactions #ACID
👍1
Оптимизация работы с транзакциями

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

1. Минимизация времени жизни транзакции

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

Чтобы уменьшить время жизни транзакции, нужно:
Выполнять только необходимые операции в рамках транзакции.
Исключить операции, не связанные с базой данных, из транзакции (например, обработку данных, вызовы внешних API).


Неоптимальный код:
@Transactional
public void processOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();

// Обработка заказа (долгая операция)
processOrderDetails(order);

order.setStatus("PROCESSED");
orderRepository.save(order);
}


Оптимизированный код:
public void processOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();

// Вынесение обработки за пределы транзакции
processOrderDetails(order);

updateOrderStatus(order);
}

@Transactional
public void updateOrderStatus(Order order) {
order.setStatus("PROCESSED");
orderRepository.save(order);
}


2. Использование правильного уровня изоляции

Высокие уровни изоляции, такие как SERIALIZABLE, обеспечивают максимальную целостность данных, но снижают производительность из-за блокировок. Используйте самый низкий уровень изоляции, который удовлетворяет требованиям:
READ COMMITTED — подходит для большинства приложений.
REPEATABLE READ или SERIALIZABLE — используйте только для критичных операций.


Пример настройки уровня изоляции:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void performOperation() {
// Логика работы с базой данных
}


3. Использование транзакций только при необходимости

Транзакции нужны далеко не для всех операций. Например, операции только для чтения могут быть выполнены без транзакции.
@Transactional(readOnly = true)
public List<Order> getOrders() {
return orderRepository.findAll();
}


Преимущества readOnly = true:
Уменьшает накладные расходы.
Улучшает производительность запросов.


4. Группировка операций

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


Без группировки:
@Transactional
public void updateCustomerInfo(Customer customer) {
customerRepository.save(customer);
}

@Transactional
public void updateOrderInfo(Order order) {
orderRepository.save(order);
}


С группировкой:
@Transactional
public void updateCustomerAndOrder(Customer customer, Order order) {
customerRepository.save(customer);
orderRepository.save(order);
}


5. Lazy Loading и транзакции


Использование ленивой загрузки (Lazy Loading) может вызвать ошибки при работе с транзакциями, если транзакция закрыта до момента доступа к связанным данным.

@Transactional
public Order getOrderWithDetails(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.getDetails().size(); // Инициализация ленивой коллекции внутри транзакции
return order;
}


Без правильной настройки или за пределами транзакции ленивые коллекции могут привести к LazyInitializationException.

#Java #Training #Spring #Transactions
👍1
Нюансы работы с транзакциями в Spring

Работа с транзакциями в Spring — это не только использование аннотации @Transactional, но и знание различных особенностей и подводных камней.

1. Работа
@Transactional с внутренними вызовами

Аннотация @Transactional применима только при вызовах методов из внешнего контекста. Внутренние вызовы методов одного класса не активируют механизмы транзакции.

@Service
public class ExampleService {

@Transactional
public void method1() {
method2(); // method2 вызван напрямую, @Transactional не сработает
}

@Transactional
public void method2() {
// Логика
}
}
Решение: Вынести вызов метода в другой бин.


2. Проксирование и прокси Spring

Spring использует динамические прокси или CGLIB для управления транзакциями. Это означает, что транзакционная логика добавляется через прокси-объект.


Динамические прокси работают только с интерфейсами.
CGLIB-прокси работают с классами и методами.
Если транзакции не работают, убедитесь, что используется корректный тип прокси.


Конфигурация прокси:

@EnableTransactionManagement(proxyTargetClass = true)
public class AppConfig {
// Конфигурация
}


3. Вложенные транзакции

Spring поддерживает вложенные транзакции с использованием PROPAGATION_NESTED. Это полезно, когда нужно откатить только часть операций, если в ней возникла ошибка.
@Transactional
public void mainTransaction() {
try {
nestedTransaction();
} catch (Exception e) {
// Ошибка в вложенной транзакции
}
}

@Transactional(propagation = Propagation.NESTED)
public void nestedTransaction() {
// Вложенная транзакция
throw new RuntimeException("Ошибка во вложенной транзакции");
}
Если возникает ошибка в nestedTransaction(), основная транзакция продолжит выполнение.


4. Тайм-ауты транзакций

Для предотвращения долгих операций можно задать тайм-аут транзакции:
@Transactional(timeout = 5)
public void processWithTimeout() {
// Транзакция завершится, если длится более 5 секунд
}


5. Работа с транзакциями в тестах

Spring позволяет удобно тестировать транзакционные методы. Используйте
@Transactional в тестах для автоматического отката изменений после выполнения теста.
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import org.junit.jupiter.api.Test;

@SpringBootTest
public class TransactionalTest {

@Test
@Transactional
public void testTransaction() {
// Изменения в базе данных
}
// После завершения теста транзакция откатывается
}


#Java #Training #Spring #Transactions
👍1