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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Простое Web приложение с асинхронной работой методов на Spring. Встреча от 24.11.2024

Запись нашей сегодняшней встречи -
YOUTUBE
RUTUBE

Спасибо всем кто пришел, за участие!💪

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

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


Смотрите, комментируйте, задавайте вопросы! Обязательно подписывайтесь на ютуб и рутюб каналы!!!

Ссылка на GitHub - https://github.com/Oleborn/MultiThreadingSpringApp

Всем хорошего настроения! 🫡✌️
Уровни изоляции транзакций и виды проблем

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

Уровни изоляции транзакций

SQL определяет четыре уровня изоляции. Каждый из них предлагает определенный баланс между производительностью и уровнем защиты от проблем параллелизма:

READ UNCOMMITTED (чтение незафиксированных данных)

Самый низкий уровень изоляции.
Транзакция может читать данные, которые еще не зафиксированы другой транзакцией.

Возможные проблемы: грязное чтение.

READ COMMITTED (чтение зафиксированных данных)

Гарантирует, что транзакция читает только данные, которые были зафиксированы.
Предотвращает грязное чтение, но не решает проблему неповторяемого чтения.


REPEATABLE READ (повторяемое чтение)

Обеспечивает, что данные, прочитанные в рамках транзакции, не изменятся, пока она не завершится.
Предотвращает грязное чтение и неповторяемое чтение, но не защищает от фантомных чтений.


SERIALIZABLE (серилизуемость)

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


Виды проблем параллелизма

Грязное чтение (Dirty Read)
Одна транзакция читает данные, которые были изменены другой транзакцией, но не зафиксированы. Если вторая транзакция откатится, первая окажется с некорректными данными.
// Транзакция 1
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

// Транзакция 2 (до фиксации транзакции 1)
SELECT balance FROM accounts WHERE id = 1;


Неповторяемое чтение (Non-repeatable Read)
Данные, прочитанные одной транзакцией, могут быть изменены другой транзакцией до завершения первой.
// Транзакция 1
SELECT balance FROM accounts WHERE id = 1;

// Транзакция 2
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

// Транзакция 1 (повторное чтение)
SELECT balance FROM accounts WHERE id = 1;


Фантомное чтение (Phantom Read)
Транзакция читает набор строк, но другой транзакцией в этот набор добавляются новые строки.
// Транзакция 1
SELECT * FROM orders WHERE amount > 100;

// Транзакция 2
INSERT INTO orders (id, amount) VALUES (10, 150);

// Транзакция 1 (повторное чтение)
SELECT * FROM orders WHERE amount > 100;


Установка уровней изоляции в Spring

Уровень изоляции можно задать с помощью аннотации @Transactional:
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

@Transactional(isolation = Isolation.READ_COMMITTED)
public void processOrder() {
// Логика обработки заказа
}
}


Spring поддерживает следующие уровни:
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE


#Java #Training #Spring #Isolation_Levels
Что выведет код?

public class Task251124_1 {
public static void main(String[] args) {
String str = "aabbccddeeff";
String result = str.replace("aa", "x")
.replace("ee", "y")
.replace("xx", "z")
.replace("dd", "w");
System.out.println(result);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
27%
zbbccwfyff
7%
xbbccwfyff
0%
xbbccdyff
67%
xbbccwyff
"Френдли" на максимум 🤪😂😂😂

https://t.me/Java_for_beginner_dev

#Mems
Управление транзакциями вручную

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

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

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

Пример: Использование 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 ManualTransactionService {

@Autowired
private PlatformTransactionManager transactionManager;

public void performManualTransaction() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("CustomTransaction");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = transactionManager.getTransaction(def);

try {
// Выполнение операций в транзакции
performDatabaseOperation1();
performDatabaseOperation2();

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

private void performDatabaseOperation1() {
// Логика операции 1
}

private void performDatabaseOperation2() {
// Логика операции 2
}
}
В этом примере транзакция начинается вручную, и ее выполнение зависит от логики программы. Если возникает ошибка, транзакция откатывается.


Программное управление в сочетании с TransactionTemplate

Spring предоставляет еще один инструмент для управления транзакциями вручную — TransactionTemplate. Это упрощает управление транзакциями за счет предоставления шаблонного метода для выполнения кода.

Пример с использованием TransactionTemplate:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

@Service
public class TemplateTransactionService {

@Autowired
private TransactionTemplate transactionTemplate;

public void executeInTransaction() {
transactionTemplate.execute(status -> {
try {
performDatabaseOperation1();
performDatabaseOperation2();
} catch (Exception ex) {
status.setRollbackOnly(); // Указание на откат транзакции
throw ex;
}
return null;
});
}

private void performDatabaseOperation1() {
// Логика операции 1
}

private void performDatabaseOperation2() {
// Логика операции 2
}
}


#Java #Training #Spring #ManualTransactionService
Когда использовать ручное управление транзакциями?

Тонкий контроль над транзакциями: Если нужно управлять транзакцией на уровне нескольких сервисов или разных контекстов.
Динамическое управление: Когда параметры транзакции (например, уровень изоляции) должны изменяться в зависимости от условий.
Исключение из стандартного поведения: Например, если нельзя использовать
@Transactional из-за вызовов методов внутри одного класса.

#Java #Training #Spring #ManualTransactionService
Rollback и Commit транзакций

Транзакции в приложениях обеспечивают выполнение группы операций над базой данных как единого целого. Два основных действия, которые завершают транзакцию, — это commit и rollback.

Что такое Commit?

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

Пример в SQL:

BEGIN TRANSACTION;

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

COMMIT;
В этом случае изменения будут сохранены в базе данных.


Что такое Rollback?

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


Пример в SQL:
BEGIN TRANSACTION;

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

ROLLBACK;
В этом случае все изменения будут отменены.


Rollback и Commit в Spring

В Spring транзакции можно управлять как вручную, так и декларативно с помощью аннотации @Transactional.

Пример ручного управления:
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 ManualTransactionService {

@Autowired
private PlatformTransactionManager transactionManager;

public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);

try {
// Дебет с одного счета
debitAccount(fromAccountId, amount);

// Кредит на другой счет
creditAccount(toAccountId, amount);

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

private void debitAccount(Long accountId, double amount) {
// Логика дебетовой операции
}

private void creditAccount(Long accountId, double amount) {
// Логика кредитной операции
}
}


#Java #Training #Spring #Rollback #Commit
Rollback с использованием @Transactional

В декларативном подходе Spring автоматически выполняет rollback для транзакций, если метод выбрасывает RuntimeException или Error.

Пример:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PaymentService {

@Transactional
public void processPayment(Long orderId) {
try {
// Обновление информации о заказе
updateOrder(orderId);

// Обновление платежной информации
updatePayment(orderId);
} catch (Exception e) {
// Логика обработки исключения
throw new RuntimeException("Ошибка при обработке платежа");
}
}

private void updateOrder(Long orderId) {
// Логика обновления заказа
}

private void updatePayment(Long orderId) {
// Логика обновления платежа
}
}
Если возникает RuntimeException, Spring автоматически откатывает транзакцию.


Принудительный rollback

Если требуется явно указать Spring, что транзакция должна быть откатана, можно использовать TransactionAspectSupport:

import org.springframework.transaction.interceptor.TransactionAspectSupport;

public void performOperation() {
try {
// Код операции
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}


#Java #Training #Spring #Rollback #Commit
Что выведет код?

public class Task261124_1 {
public static void main(String[] args) {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Index error");
throw new RuntimeException("Runtime exception thrown");
} finally {
System.out.println("Finally block");
}
}
}


#Tasks
🧐 хороший план...😂

https://t.me/Java_for_beginner_dev

#Mems
Исключения в транзакциях

Исключения — важная часть управления транзакциями в Spring. Они влияют на поведение транзакции, определяя, будет ли она зафиксирована или откатана.

Влияние исключений на транзакции

Spring по умолчанию откатывает транзакцию при выбросе RuntimeException или Error. Однако, если выбрасывается Checked Exception, транзакция будет зафиксирована, если явно не указано иное.

Пример:
import org.springframework.transaction.annotation.Transactional;

@Transactional
public void processOrder(Long orderId) throws Exception {
// Обновление данных
updateOrder(orderId);

if (someCondition()) {
throw new Exception("Проверяемое исключение");
}
}

@Transactional
public void processPayment(Long paymentId) {
// Обновление платежа
updatePayment(paymentId);

if (someCondition()) {
throw new RuntimeException("Непроверяемое исключение");
}
}
В методе processOrder транзакция не будет откатана при выбросе Exception.
В методе processPayment транзакция будет откатана из-за RuntimeException.


Управление rollback для Checked Exception

Если нужно, чтобы транзакция откатывалась при выбросе Checked Exception, можно использовать параметр rollbackFor аннотации @Transactional:
import org.springframework.transaction.annotation.Transactional;

@Transactional(rollbackFor = Exception.class)
public void processOrder(Long orderId) throws Exception {
// Обновление данных
updateOrder(orderId);

if (someCondition()) {
throw new Exception("Проверяемое исключение");
}
}
Теперь транзакция откатится, даже если выброшено проверяемое исключение.


Исключения, при которых не происходит rollback

Иногда нужно исключить определенные исключения из списка тех, которые вызывают откат транзакции. Это можно сделать с помощью параметра noRollbackFor.

Пример:
@Transactional(noRollbackFor = IllegalArgumentException.class)
public void performOperation() {
// Код операции

if (someCondition()) {
throw new IllegalArgumentException("Некорректные данные");
}
}
В этом случае транзакция будет зафиксирована, даже если выбросить IllegalArgumentException.


Обработка исключений в методах с @Transactional

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

Пример:
@Service
public class ExampleService {

@Transactional
public void method1() {
// Операции в транзакции
method2(); // Не будет выполнено в рамках транзакции
}

@Transactional
public void method2() {
// Операции в транзакции
}
}


Принудительный откат при исключении

Для явного указания отката транзакции, даже если исключение не требует этого, можно использовать TransactionAspectSupport:
import org.springframework.transaction.interceptor.TransactionAspectSupport;

public void executeWithRollback() {
try {
// Код операции
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw e;
}
}


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

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

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
Что выведет код?

class CustomObject {
private String value;

public CustomObject(String value) {
this.value = value;
}

@Override
public String toString() {
return "Value: " + value;
}
}

public class Task271124_1 {
public static void main(String[] args) {
CustomObject obj1 = new CustomObject("Test");
CustomObject obj2 = new CustomObject("Test");
Object obj3 = obj1;

System.out.println(obj1.equals(obj2));
System.out.println(obj1 == obj2);
System.out.println(obj1 == obj3);
System.out.println(obj3.toString());
}
}


#Tasks
И не детям в принципе...🧐😂

https://t.me/Java_for_beginner_dev

#Mems
Нюансы работы с транзакциями в 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
Введение в кеширование в Spring

Кеширование (caching) – это техника оптимизации производительности, которая позволяет сохранять результаты вычислений или запросов для их повторного использования, избегая лишних операций. В Spring кеширование реализуется просто и эффективно благодаря интеграции с Spring Cache.

Как работает кеширование?

При запросе данных приложение сначала проверяет, есть ли результат в кеше:
Если данные найдены, они возвращаются напрямую из кеша.
Если данных нет, выполняется вычисление (или запрос к базе данных), результат сохраняется в кеше и возвращается пользователю.


Spring Cache – основы

Spring Cache предоставляет аннотации и интерфейсы для простого внедрения кеширования.

Основные компоненты:
Кеш-менеджер (Cache Manager) – отвечает за создание и управление кешами.
Аннотации – для указания, какие методы или результаты кешировать.


Spring поддерживает различные провайдеры кеша, например:
ConcurrentMapCache (в памяти, по умолчанию),
Ehcache,
Redis,
Caffeine.


Подключение кеша в Spring

Для начала нужно включить кеширование в вашем приложении:

@Configuration
@EnableCaching // Включаем поддержку кеширования
public class CacheConfig {
// Конфигурация кеша, если требуется (например, Redis или Ehcache)
}


Создадим сервис, который возвращает данные из базы:
@Service
public class DataService {
public String getDataById(Long id) {
// Имитация долгого выполнения, например, запроса к БД
try {
Thread.sleep(2000); // Задержка 2 секунды
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Data for ID: " + id;
}
}


Теперь добавим кеш:
@Service
public class CachedDataService {
@Cacheable("dataCache") // Кешируем результат метода
public String getDataById(Long id) {
// Имитация долгого выполнения
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Cached data for ID: " + id;
}
}


#Java #Training #Spring #Caching