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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Аннотация @Version

Аннотация @Version используется в Java Persistence API (JPA) для реализации оптимистичной блокировки (optimistic locking) в сущностях. Она находится в пакете javax.persistence (или jakarta.persistence, если используется Jakarta EE). Эта аннотация применяется к полю сущности, которое хранит версию записи. Поле с аннотацией @Version автоматически увеличивается при каждом обновлении сущности, что позволяет предотвратить проблемы, связанные с параллельными изменениями данных.

Аннотация @Version не принимает параметров. Она используется как маркер для указания поля, которое будет использоваться для хранения версии сущности.

Поле должно быть одного из следующих типов:
int
Integer
long
Long
short
Short
java.sql.Timestamp


Пример использования:
@Version
private int version;


При создании новой сущности значение поля с аннотацией @Version инициализируется как 0 (для числовых типов) или текущее время (для java.sql.Timestamp).
При каждом обновлении сущности значение поля автоматически увеличивается на 1 (для числовых типов) или обновляется до текущего времени (для java.sql.Timestamp).

Жизненный цикл

Применение: Аннотация @Version применяется на этапе маппинга сущности JPA. Она используется при создании таблиц в базе данных (если используется автоматическая генерация схемы) и при выполнении операций чтения/записи данных.
Инициализация: При создании новой сущности значение поля инициализируется в соответствии с типом данных.
Обновление: При каждом обновлении сущности значение поля автоматически увеличивается или обновляется.
Проверка: При обновлении сущности JPA проверяет, что значение поля
@Version в базе данных совпадает с значением в сущности. Если значения не совпадают, выбрасывается исключение OptimisticLockException.

Механизмы Spring и настройки Spring Boot

JPA и Hibernate: Spring Data JPA использует провайдеров JPA (например, Hibernate) для работы с базой данных. Аннотация @Version обрабатывается на уровне JPA, но Spring обеспечивает интеграцию с JPA через EntityManager и репозитории.
Оптимистичная блокировка: Spring Data JPA автоматически поддерживает оптимистичную блокировку, используя поле с аннотацией
@Version.

Настройки Spring Boot

Spring Boot упрощает настройку JPA через свойства конфигурации. Вот несколько ключевых настроек, которые могут влиять на работу @Version:

Генерация схемы базы данных:
Если используется автоматическая генерация схемы (например, через Hibernate), Spring Boot настраивает это через свойство:
spring.jpa.hibernate.ddl-auto=update


В этом случае Hibernate создаст столбец для поля @Version с типом данных, соответствующим типу поля (например, INT для int или BIGINT для long).

Настройки Hibernate:
Spring Boot позволяет настраивать Hibernate через свойства, например:
spring.jpa.properties.hibernate.format_sql=true
Эти настройки не влияют напрямую на @Version, но могут помочь в отладке SQL-запросов, связанных с оптимистичной блокировкой.


Обработка исключений:
Spring Boot автоматически обрабатывает исключение OptimisticLockException, которое может быть выброшено при конфликте версий. Это исключение можно обработать в коде или через механизмы Spring (например, @ExceptionHandler).

Варианты настройки

Использование числовых типов:
Рекомендуется использовать int или long для поля @Version, так как это упрощает работу с версиями и уменьшает вероятность ошибок.

Использование java.sql.Timestamp:
Может быть полезно, если требуется использовать временные метки для контроля версий. Однако это менее предсказуемо, чем числовые типы.

Кастомизация обработки исключений:
Можно настроить обработку OptimisticLockException для предоставления пользователю информации о конфликте версий.

#Java #Training #Hard #Spring #SpringDataJPA #Version
Аннотация @Version

Аннотация @Version используется в JPA для реализации оптимистичной блокировки (optimistic locking) - механизма контроля параллельного доступа к данным без использования явных блокировок на уровне базы данных. Находится в пакете javax.persistence.

Параметры аннотации

Аннотация не имеет параметров. Применяется к единственному полю сущности, которое должно быть:
Числовым типом (int, Integer, long, Long)
Типом java.sql.Timestamp


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

@Entity
public class Product {
@Id
private Long id;

@Version
private Integer version;

// другие поля
}


Жизненный цикл аннотации

Инициализация:
При создании сущности поле получает значение 0 (или текущее время для Timestamp)

Обновление:
При каждом обновлении сущности значение автоматически увеличивается на 1
JPA проверяет, что значение в БД совпадает с исходным значением перед обновлением


Конфликт:
Если значение в БД изменилось (другой транзакцией), выбрасывается OptimisticLockException

Механизм работы

При чтении сущности:
Product product = em.find(Product.class, 1L);
// product.version = 1 (текущее значение в БД)


При обновлении:
product.setPrice(newPrice);
em.merge(product);


Выполняется SQL вида:
UPDATE product 
SET ..., version = version + 1
WHERE id = 1 AND version = 1


Если версия изменилась:
Количество обновленных строк = 0 → OptimisticLockException

Обработка исключений:

@ExceptionHandler(OptimisticLockException.class)
public ResponseEntity<String> handleConflict(OptimisticLockException ex) {
return ResponseEntity.status(HttpStatus.CONFLICT).body("Объект был изменен другим пользователем");
}


Практическое использование

Базовый сценарий:

@Service
@Transactional
public class ProductService {
public void updatePrice(Long id, BigDecimal newPrice) {
Product product = productRepository.findById(id).orElseThrow();
product.setPrice(newPrice);
// При коммите транзакции версия автоматически увеличится
}
}


Проверка версии вручную:

public void updateWithVersionCheck(Long id, Integer expectedVersion, Product update) {
Product product = productRepository.findById(id).orElseThrow();
if (!product.getVersion().equals(expectedVersion)) {
throw new OptimisticLockException("Версия не совпадает");
}
// ... обновление
}


Использование DTO:
public class ProductDto {
private Long id;
private Integer version;
// другие поля

public Product toEntity() {
Product product = new Product();
product.setId(this.id);
product.setVersion(this.version);
// ...
return product;
}
}


Особенности реализации

Типы полей:
Для числовых типов: последовательное увеличение
Для Timestamp: текущее время сервера

Поведение при:
Создании: устанавливается 0
Клонировании: версия не копируется
Удалении: проверка версии не выполняется (настраивается через
@OptimisticLocking)

Производительность:
Минимальные накладные расходы
Не требует блокировок в БД


Альтернативы и дополнения

Пессимистичная блокировка:
@Lock(LockModeType.PESSIMISTIC_WRITE)
Product findByIdForUpdate(Long id);


Дополнительные аннотации Hibernate:
@OptimisticLocking - для настройки поведения
@DynamicUpdate - обновлять только измененные поля

Кастомные стратегии:
Реализация собственного механизма через @PreUpdate

#Java #Training #Hard #Spring #SpringDataJPA #Version