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

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

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

Аннотация @IdClass используется в JPA (Java Persistence API) для указания класса, который представляет составной первичный ключ сущности. Она применяется, когда сущность имеет несколько полей, которые вместе образуют уникальный идентификатор (составной ключ). Аннотация находится в пакете javax.persistence.

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

value (обязательный):
Тип: Class<?>.
Описание: Указывает класс, который представляет составной первичный ключ. Этот класс должен быть сериализуемым, иметь конструктор по умолчанию и переопределять методы equals() и hashCode().


Пример:

@IdClass(UserId.class)
public class User {
@Id
private Long departmentId;

@Id
private Long userId;

// Остальные поля и методы
}


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

Инициализация:
Аннотация @IdClass обрабатывается во время инициализации JPA-провайдера (например, Hibernate). JPA-провайдер анализирует сущность и создает метаданные для составного ключа.

Выполнение:
Когда сущность сохраняется, обновляется или извлекается из базы данных, JPA-провайдер использует класс, указанный в @IdClass, для работы с составным ключом.

Уничтожение:
Аннотация не имеет явного жизненного цикла уничтожения, так как она используется только для конфигурации составного ключа.

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

Использование составного ключа:
Класс, представляющий составной ключ, должен быть сериализуемым, иметь конструктор по умолчанию и переопределять методы equals() и hashCode():

public class UserId implements Serializable {
private Long departmentId;
private Long userId;

// Конструктор по умолчанию
public UserId() {}

// Конструктор с параметрами
public UserId(Long departmentId, Long userId) {
this.departmentId = departmentId;
this.userId = userId;
}

// Геттеры и сеттеры

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserId userId1 = (UserId) o;
return Objects.equals(departmentId, userId1.departmentId) &&
Objects.equals(userId, userId1.userId);
}

@Override
public int hashCode() {
return Objects.hash(departmentId, userId);
}
}


Использование с @Embeddable:
Вместо @IdClass можно использовать аннотацию @Embeddable для определения составного ключа:

@Embeddable
public class UserId implements Serializable {
private Long departmentId;
private Long userId;

// Конструктор по умолчанию, геттеры, сеттеры, equals, hashCode
}

@Entity
public class User {
@EmbeddedId
private UserId id;

// Остальные поля и методы
}


Использование с репозиториями:
Репозитории Spring Data JPA могут работать с сущностями, имеющими составные ключи:

public interface UserRepository extends JpaRepository<User, UserId> {
// Методы репозитория
}


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

Аннотация @MapsId используется в JPA (Java Persistence API) для указания, что внешний ключ сущности также является её первичным ключом. Это полезно при работе с отношениями "один к одному" или "многие к одному", где дочерняя сущность использует первичный ключ родительской сущности в качестве своего собственного первичного ключа. Аннотация находится в пакете javax.persistence.

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

value (необязательный):
Тип: String.
Значение по умолчанию: "".
Описание: Указывает имя атрибута сущности, который является первичным ключом. Если не указано, используется первичный ключ родительской сущности.


Пример:
@MapsId("userId")
@OneToOne
@JoinColumn(name = "user_id")
private User user;


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

Инициализация:
Аннотация @MapsId обрабатывается во время инициализации JPA-провайдера (например, Hibernate). JPA-провайдер анализирует сущность и создает метаданные для связи с родительской сущностью.

Выполнение:
Когда сущность сохраняется, обновляется или извлекается из базы данных, JPA-провайдер использует первичный ключ родительской сущности в качестве первичного ключа дочерней сущности.

Уничтожение:
Аннотация не имеет явного жизненного цикла уничтожения, так как она используется только для конфигурации связи с родительской сущностью.

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

Использование с @OneToOne:
Аннотация @MapsId часто используется с отношением "один к одному":

@Entity
public class UserProfile {
@Id
private Long userId;

@MapsId
@OneToOne
@JoinColumn(name = "user_id")
private User user;

// Остальные поля и методы
}


Использование с @ManyToOne:
Аннотация @MapsId также может использоваться с отношением "многие к одному":
@Entity
public class Order {
@Id
private Long orderId;

@MapsId("userId")
@ManyToOne
@JoinColumn(name = "user_id")
private User user;

// Остальные поля и методы
}


Использование с составными ключами:
Аннотация
@MapsId может использоваться с составными ключами, если родительская сущность имеет составной ключ:
@Entity
public class UserProfile {
@EmbeddedId
private UserProfileId id;

@MapsId("userId")
@ManyToOne
@JoinColumns({
@JoinColumn(name = "department_id", referencedColumnName = "departmentId"),
@JoinColumn(name = "user_id", referencedColumnName = "userId")
})
private User user;

// Остальные поля и методы
}


Использование с репозиториями:
Репозитории Spring Data JPA могут работать с сущностями, имеющими аннотацию @MapsId:

public interface UserProfileRepository extends JpaRepository<UserProfile, Long> {
// Методы репозитория
}


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

Аннотация @CreatedBy используется в Spring Data для автоматического заполнения поля информацией о пользователе, создавшем сущность. Это часть механизма аудита Spring Data, позволяющего автоматически отслеживать, кто и когда создавал или изменял сущности. Аннотация находится в пакете org.springframework.data.annotation.

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

Аннотация @CreatedBy не имеет параметров. Она применяется к полям сущности, которые должны автоматически заполняться информацией о создателе.

Пример использования:
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Document {
@CreatedBy
private String createdBy;

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


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

Инициализация:
Аннотация активируется при включении аудита через @EnableJpaAuditing
Требуется регистрация AuditingEntityListener для сущности


Выполнение:
Перед сохранением новой сущности (при вызове save())
Spring автоматически определяет текущего пользователя и устанавливает значение поля


Обновление:
Поле заполняется только при создании сущности
Не изменяется при последующих обновлениях


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

AuditingEntityListener:
Класс, обрабатывающий события жизненного цикла сущности
Автоматически заполняет аннотированные поля


AuditorAware:
Интерфейс для получения информации о текущем пользователе
Требуется реализация для работы
@CreatedBy

EnableJpaAuditing:
Аннотация для активации механизма аудита

Настройки Spring Boot

Активация аудита:
@Configuration
@EnableJpaAuditing
public class AuditConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.map(Authentication::getName);
}
}


Конфигурация сущности:
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Order {
@CreatedBy
private String createdBy;

@CreatedDate
private LocalDateTime createdDate;

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


Настройки безопасности (если используется Spring Security):
spring.security.user.name=admin
spring.security.user.password=secret


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

Типы полей:
Может использоваться с любым типом (String, Long, UUID)
Чаще всего используется String для хранения имени пользователя


Интеграция с Spring Security:
Автоматическое получение имени текущего пользователя

Пример реализации AuditorAware:
public class SecurityAuditorAware implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
return Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getName);
}
}


Кастомные реализации:
Возможность использовать ID пользователя вместо имени
Интеграция с кастомными системами аутентификации


Комбинирование с другими аннотациями аудита:
@CreatedBy
private String createdBy;

@CreatedDate
private LocalDateTime createdDate;

@LastModifiedBy
private String lastModifiedBy;

@LastModifiedDate
private LocalDateTime lastModifiedDate;


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

Аннотация @CreatedDate используется в Spring Data для автоматического заполнения поля датой и временем создания сущности. Это часть механизма аудита Spring Data, который позволяет автоматически отслеживать временные метки создания и модификации сущностей. Аннотация находится в пакете org.springframework.data.annotation.

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

Аннотация @CreatedDate не имеет параметров. Она применяется к полям сущности, которые должны автоматически заполняться временем создания.

Пример использования:
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Document {
@CreatedDate
private LocalDateTime createdAt;

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


Поддерживаемые типы данных

Поле с аннотацией @CreatedDate может быть одного из следующих типов:
java.time.LocalDateTime
java.time.LocalDate
java.time.ZonedDateTime
java.util.Date
java.util.Calendar
java.sql.Timestamp
Long (для timestamp в миллисекундах)


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

Инициализация:
Активируется при включении аудита через @EnableJpaAuditing
Требуется регистрация AuditingEntityListener для сущности


Выполнение:
Автоматически устанавливает текущую дату и время при первом сохранении сущности
Значение устанавливается один раз при создании и не изменяется при последующих обновлениях


Обновление:
Для отслеживания времени изменения следует использовать @LastModifiedDate

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

AuditingEntityListener:
Обрабатывает события жизненного цикла сущности
Автоматически заполняет аннотированные поля датой/временем


EnableJpaAuditing:
Аннотация для активации механизма аудита

Настройки Spring Boot

Активация аудита в конфигурационном классе:
@Configuration
@EnableJpaAuditing
public class AuditConfig {
// Дополнительные настройки при необходимости
}


Конфигурация сущности:
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Order {
@CreatedDate
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;

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


Настройка часового пояса (опционально):
spring.jpa.properties.hibernate.jdbc.time_zone=UTC


Варианты настройки и использования

Комбинирование с другими аннотациями аудита:
@CreatedDate
private LocalDateTime createdAt;

@CreatedBy
private String createdBy;

@LastModifiedDate
private LocalDateTime updatedAt;

@LastModifiedBy
private String updatedBy;


Использование разных типов даты/времени:
@CreatedDate
private Date createdAt; // java.util.Date

@CreatedDate
private Long createdTimestamp; // timestamp в миллисекундах


Кастомные форматы даты (при использовании с DTO):

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@CreatedDate
private LocalDateTime createdAt;


Использование в проекциях Spring Data:

public interface OrderProjection {
@Value("#{target.createdAt}")
String getFormattedCreatedAt();
}


Особенности работы

Одноразовое заполнение:

Значение устанавливается только при первом сохранении сущности
Не изменяется при последующих обновлениях


Точность времени:
Зависит от базы данных и JPA-провайдера
Для высокой точности рекомендуется использовать java.time типы


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

#Java #Training #Hard #Spring #SpringDataJPA #CreatedDate
Аннотации @LastModifiedBy и @LastModifiedDate

Эти аннотации являются частью механизма аудита Spring Data и используются для автоматического отслеживания:
@LastModifiedBy - кто последним изменял сущность
@LastModifiedDate - когда были внесены последние изменения

Обе аннотации находятся в пакете org.springframework.data.annotation.

Аннотация @LastModifiedBy

Параметры
Как и @CreatedBy, аннотация не имеет параметров. Применяется к полям, которые должны хранить информацию о последнем редакторе.

Поддерживаемые типы
String (для имен пользователей)
Long/UUID (для ID пользователей)

Любой другой тип, возвращаемый реализацией AuditorAware

Пример использования
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Document {
@LastModifiedBy
private String lastModifiedBy;

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


Аннотация @LastModifiedDate

Параметры
Не имеет параметров. Применяется к полям даты/времени.

Поддерживаемые типы
Те же, что и для @CreatedDate:
java.time типы (LocalDateTime, ZonedDateTime)
java.util.Date
Long (timestamp)


Пример использования
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Document {
@LastModifiedDate
private LocalDateTime lastModifiedAt;

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


Комбинированное использование

Полный пример аудита
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Product {
@Id
private Long id;

@CreatedBy
private String createdBy;

@CreatedDate
private LocalDateTime createdAt;

@LastModifiedBy
private String lastModifiedBy;

@LastModifiedDate
private LocalDateTime lastModifiedAt;

// Бизнес-поля
private String name;
private BigDecimal price;
}


Особенности поведения

Инициализация:
@CreatedBy/Date заполняются только при создании
@LastModifiedBy/Date обновляются при каждом изменении

Сценарий обновления:
product.setPrice(new BigDecimal("99.99"));
productRepository.save(product);
Поля lastModifiedBy/At будут автоматически обновлены
Поля createdBy/At останутся без изменений


Настройка в Spring Boot

Обязательная конфигурация
Активация аудита:
@SpringBootApplication
@EnableJpaAuditing
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}


Реализация AuditorAware (для @CreatedBy и @LastModifiedBy):
@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getName);
}


Дополнительные настройки
Для JPA репозиториев:
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true


Настройка формата даты (опционально):
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=UTC


Практические рекомендации


Для веб-приложений:
Комбинируйте с Spring Security для автоматического определения пользователя
Используйте DTO с аннотацией
@JsonFormat для красивого отображения дат

Для микросервисов:
Вместо имени пользователя можно хранить ID сервиса
Используйте единый часовой пояс (UTC) для всех сервисов


Оптимизация запросов:
@Query("SELECT p FROM Product p WHERE p.lastModifiedAt > :since")
List<Product> findRecentlyModified(@Param("since") LocalDateTime since);


Интеграция с историей изменений:
Можно создать отдельную таблицу для хранения полной истории изменений
Использовать
@PreUpdate и @PrePersist для дополнительного логирования

Ограничения и решения

Проблема: Не обновляется при косвенных изменениях
Решение: Явно вызывать save() или использовать
@Version

Проблема: Неточное время в кластере
Решение: Использовать NTP или временные сервисы


Проблема: Кастомные редакторы (например, системные задания)
Решение: Переопределить AuditorAware для специфических случаев


#Java #Training #Hard #Spring #SpringDataJPA #LastModifiedBy #LastModifiedDate
Аннотация @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
Аннотация @PersistenceContext

Аннотация @PersistenceContext используется для внедрения (inject) контейнера управляемых JPA-сущностей (EntityManager) в Spring-приложение. Она принадлежит к спецификации Jakarta Persistence API (JPA) и находится в пакете jakarta.persistence.

Spring автоматически управляет жизненным циклом EntityManager, обеспечивая корректное открытие и закрытие сессий, а также интеграцию с транзакциями.

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

name (необязательный)
Тип: String
Описание: Имя единицы персистентности (persistence-unit), если их несколько в persistence.xml.


Пример:
@PersistenceContext(name = "myPersistenceUnit")
private EntityManager entityManager;


unitName (необязательный)
Тип: String
Описание: Альтернатива name, явно указывает имя persistence-unit.

Пример:
@PersistenceContext(unitName = "myPersistenceUnit")
private EntityManager entityManager;


type (необязательный)
Тип: PersistenceContextType (TRANSACTION или EXTENDED)
Значение по умолчанию: TRANSACTION


Описание:

TRANSACTION (по умолчанию): EntityManager привязан к текущей транзакции и закрывается после её завершения.
EXTENDED: EntityManager живет дольше транзакции (используется в Stateful-бинах, например, в JSF).


Пример:
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;


properties (необязательный)
Тип: Map<String, Object>
Описание: Дополнительные свойства для настройки EntityManager (например, hibernate.jdbc.batch_size).


Пример:
@PersistenceContext(properties = {
@PersistenceProperty(name = "hibernate.jdbc.batch_size", value = "20")
})
private EntityManager entityManager;


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

Инициализация
При старте Spring создает прокси для EntityManager, который делегирует вызовы реальному EntityManager только внутри транзакции.
Если используется EXTENDED, EntityManager сохраняет состояние между транзакциями.


Работа в транзакции
В режиме TRANSACTION (по умолчанию) EntityManager автоматически открывается при старте транзакции (@Transactional) и закрывается после её завершения.

Уничтожение
В режиме TRANSACTION EntityManager закрывается после завершения транзакции.
В режиме EXTENDED EntityManager живет до уничтожения бина (например, при завершении HTTP-сессии в веб-приложении).


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

1. Как Spring Boot настраивает EntityManager?

Spring Boot автоматически создает EntityManagerFactory на основе:
Настроек DataSource (из application.properties).
JPA-провайдера (Hibernate по умолчанию).


2. Альтернативные варианты настройки

Несколько источников данных:
Если нужно несколько EntityManager, можно определить несколько @Bean в конфигурации:
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.example.domain");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
return em;
}


Ручное управление транзакциями:
Можно использовать EntityManager без @Transactional, но тогда нужно вручную вызывать em.getTransaction().begin() и commit().

Аннотация @PersistenceUnit

Аннотация @PersistenceUnit используется для внедрения EntityManagerFactory (а не самого EntityManager). Полезна, когда требуется создавать EntityManager вручную.

Параметры

unitName (необязательный) – имя persistence-unit (если их несколько).

Пример использования
@PersistenceUnit
private EntityManagerFactory emf;

public void someMethod() {
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
// ... операции с БД ...
em.getTransaction().commit();
} finally {
em.close();
}
}


Когда использовать @PersistenceUnit вместо @PersistenceContext?

Если нужно полное управление жизненным циклом EntityManager (например, в batch-обработке).
Если требуется несколько EntityManager в одном методе.

#Java #Training #Hard #Spring #SpringDataJPA #PersistenceUnit #PersistenceContext
Аннотация @EnableJpaAuditing

Аннотация @EnableJpaAuditing активирует поддержку аудита сущностей JPA в Spring Data. Она позволяет автоматически отслеживать изменения в сущностях, такие как:
Кто создал или изменил запись (createdBy, lastModifiedBy).
Когда запись была создана или изменена (createdDate, lastModifiedDate).


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

auditorAwareRef (необязательный)
Тип: String
Описание: Указывает имя бина, реализующего интерфейс AuditorAware<T>. Этот бин предоставляет информацию о текущем пользователе (например, из Spring Security).


Пример:
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
@Configuration
public class JpaConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.of("admin"); // Возвращает текущего пользователя
}
}


dateTimeProviderRef (необязательный)
Тип: String
Описание: Указывает имя бина, реализующего DateTimeProvider. Позволяет кастомизировать логику получения текущей даты/времени (например, для тестов).


Пример:
@EnableJpaAuditing(dateTimeProviderRef = "customDateTimeProvider")
@Configuration
public class JpaConfig {
@Bean
public DateTimeProvider customDateTimeProvider() {
return () -> Optional.of(Instant.now());
}
}


setDates (необязательный)
Тип: boolean
Значение по умолчанию: true

Описание: Если false, автоматическое обновление полей createdDate и lastModifiedDate отключается.

modifyOnCreate (необязательный)
Тип: boolean
Значение по умолчанию: true
Описание: Если false, поле lastModifiedDate не обновляется при создании сущности (только createdDate).


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

Инициализация
При старте приложения Spring ищет классы с @EnableJpaAuditing и регистрирует AuditingEntityListener.
Если указан auditorAwareRef, Spring внедряет соответствующий бин.


Сохранение/обновление сущности

Перед вставкой (@PrePersist) автоматически заполняются:
createdBy и createdDate (если поддерживается).

Перед обновлением (@PreUpdate) заполняются:
lastModifiedBy и lastModifiedDate.

Уничтожение
Аудит не влияет на удаление сущностей, но можно добавить @PreRemove логику вручную.

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

1. Как работает аудит в Spring Data JPA?

AuditingEntityListener – JPA-колбек, который обрабатывает события @PrePersist и @PreUpdate.
AuditorAware – интерфейс для получения информации о текущем пользователе.
@CreatedDate, @LastModifiedDate, @CreatedBy, @LastModifiedBy – аннотации для полей сущности.

2. Настройка сущности
@Entity
@EntityListeners(AuditingEntityListener.class) // Включение аудита
public class User {
@Id
@GeneratedValue
private Long id;

@CreatedDate
private Instant createdDate;

@LastModifiedDate
private Instant lastModifiedDate;

@CreatedBy
private String createdBy;

@LastModifiedBy
private String lastModifiedBy;
}


3. Конфигурация Spring Boot

Если используется Spring Boot, можно не указывать
@EnableJpaAuditing вручную, если:
Есть бин AuditorAware.
Включен spring.data.jpa.repositories.bootstrap-mode=default (по умолчанию).


Пример application.properties:
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true


4. Интеграция с Spring Security

Если приложение использует Spring Security, AuditorAware может возвращать текущего пользователя:
@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.map(Authentication::getName);
}


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

Отключение аудита для отдельных полей
Не аннотировать поле @CreatedBy, если оно не нужно.

Кастомные даты
Реализовать DateTimeProvider, если требуется использовать не Instant.now(), а другую временную зону.

Аудит без Spring Security
Можно возвращать фиксированное значение (например, "system"):

@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.of("system");
}


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

Аннотация @EntityListeners является частью спецификации JPA (Jakarta Persistence API) и позволяет привязать кастомные слушатели (listeners) к сущностям JPA.

Эти слушатели могут перехватывать события жизненного цикла сущности, такие как:
@PrePersist (перед сохранением)
@PostPersist (после сохранения)
@PreUpdate (перед обновлением)
@PostUpdate (после обновлением)
@PreRemove (перед удалением)
@PostRemove (после удалением)
@PostLoad (после загрузкой из БД)

Аннотация находится в пакете jakarta.persistence и применяется на уровне класса сущности.

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

value (обязательный)
Тип: Массив классов (Class<?>[])
Описание: Принимает один или несколько классов-слушателей, которые должны реализовывать callback-методы JPA.

Пример:
@Entity
@EntityListeners({AuditListener.class, CacheInvalidationListener.class})
public class User {
// поля сущности
}


Жизненный цикл слушателей

Регистрация слушателей
При загрузке метаданных JPA провайдер (Hibernate) регистрирует указанные классы-слушатели для сущности.
Вызов callback-методов
При наступлении соответствующего события жизненного цикла JPA вызывает методы слушателей в порядке их объявления.


Порядок выполнения
Если для сущности определено несколько слушателей, их методы выполняются в порядке объявления в аннотации.
Глобальные слушатели (зарегистрированные в orm.xml) выполняются после локальных.


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

1. Интеграция с Spring
Spring автоматически поддерживает DI в JPA Entity Listeners, если они являются Spring-бинами.

Для этого нужно:
Пометить класс-слушатель как @Component
Зарегистрировать его в Spring-контексте


2. Пример Spring-совместимого слушателя
@Component
public class AuditListener {

@PrePersist
public void prePersist(Object entity) {
System.out.println("PrePersist: " + entity);
}

@PreUpdate
public void preUpdate(Object entity) {
System.out.println("PreUpdate: " + entity);
}
}


3. Конфигурация в Spring Boot
Spring Boot автоматически настраивает поддержку Entity Listeners через:
Автоконфигурацию JPA (HibernateJpaAutoConfiguration)
Поддержку инъекции зависимостей в слушатели


Варианты использования

1. Аудит изменений
public class AuditListener {
@PrePersist
public void setCreationDate(Object entity) {
if (entity instanceof Auditable) {
((Auditable) entity).setCreatedAt(LocalDateTime.now());
}
}
}


2. Валидация перед сохранением
public class ValidationListener {
@PrePersist
@PreUpdate
public void validate(Object entity) {
if (entity instanceof Validatable) {
((Validatable) entity).validate();
}
}
}


3. Кеширование
public class CacheListener {
@PostPersist
@PostUpdate
@PostRemove
public void invalidateCache(Object entity) {
CacheManager.evict(entity.getClass(), entity.getId());
}
}


Ограничения и особенности

Контекст выполнения
Callback-методы выполняются в том же контексте транзакции, что и основная операция.

Исключения
Выброс исключения в callback-методе приводит к откату транзакции.

Порядок выполнения
Методы выполняются в порядке:

Callback-методы самой сущности
Локальные
@EntityListeners
Глобальные слушатели


DI ограничения

В чистом JPA слушатели не поддерживают инъекцию зависимостей
В Spring DI работает только для Spring-бинов


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

Аннотация
@Projection используется в Spring Data для определения проекций - специальных интерфейсов или классов, которые позволяют выбирать только определенные поля из сущностей.

Это полезно для:
Оптимизации запросов (избегания select *)
Создания DTO на лету
Кастомизации JSON-представления REST API


Проекции находятся в пакете org.springframework.data.rest.core.config.Projection и применяются к интерфейсам или классам.


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

types (обязательный)
Тип: Class<?>[]
Описание: Указывает сущности, к которым применяется проекция


Пример:
@Projection(types = {User.class})
public interface UserSummary {
String getUsername();
String getEmail();
}


name (необязательный)
Тип: String
Описание: Задает имя проекции для использования в Spring Data REST


Пример:
@Projection(name = "summary", types = User.class)
public interface UserSummary {...}


Типы проекций

1. Интерфейсные проекции

Наиболее распространенный подход:
@Projection(types = User.class)
public interface UserView {
String getUsername();
String getEmail();

// Можно включать связанные сущности
@Value("#{target.department.name}")
String getDepartmentName();
}


2. Классовые проекции (DTO)
Используются классы вместо интерфейсов:
@Projection(types = User.class)
public class UserDto {
private final String username;
private final String email;

public UserDto(String username, String email) {
this.username = username;
this.email = email;
}

// Геттеры
}


3. Динамические проекции
Можно создавать проекции на лету через SpEL:
@Projection(types = User.class)
public interface DynamicUserView {
@Value("#{target.username + ' (' + target.email + ')'}")
String getDisplayName();
}


Жизненный цикл проекций

Объявление проекции
Создается интерфейс/класс с аннотацией @Projection
Определяются методы, соответствующие полям сущности


Использование в репозитории
Spring Data создает прокси-реализацию при выполнении запроса

Преобразование результатов
После выполнения запроса результаты маппятся на проекцию

Механизмы Spring Data

1. Автоматический маппинг

Spring Data автоматически:
Анализирует методы проекции
Создает соответствующий SQL (только выбранные поля)
Преобразует результат в проекцию


2. Интеграция с репозиториями
public interface UserRepository extends JpaRepository<User, Long> {
<T> List<T> findBy(Class<T> type);
UserSummary findSummaryById(Long id);
}


3. Поддержка в Spring Data REST

Проекции можно активировать через параметр запроса:
GET /users/1?projection=summary


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


1. Базовый пример
@Entity
public class User {
private Long id;
private String username;
private String email;
// геттеры/сеттеры
}

@Projection(types = User.class)
public interface UserSummary {
String getUsername();
String getEmail();
}

// В репозитории
UserSummary findSummaryById(Long id);


2. Комплексная проекция
@Projection(types = {User.class, Department.class})
public interface UserDetailView {
String getUsername();

@Value("#{target.department.name}")
String getDepartmentName();

default String getDisplayInfo() {
return getUsername() + " from " + getDepartmentName();
}
}


3. Вложенные проекции
@Projection(types = Order.class)
public interface OrderView {
String getOrderNumber();

@Value("#{target.customer}")
CustomerView getCustomer();
}

@Projection(types = Customer.class)
public interface CustomerView {
String getName();
}


Настройки Spring Boot

Автоконфигурация
Spring Boot автоматически настраивает поддержку проекций через SpringDataWebConfiguration

Ручная регистрация
Можно явно зарегистрировать проекции:
@Configuration
public class ProjectionConfig {
@Bean
public ProjectionDefinitionRegistrar projectionRegistrar() {
return registrar -> {
registrar.addProjection(UserSummary.class);
};
}
}


Настройка REST

В application.properties:
spring.data.rest.default-projection=summary


#Java #Training #Hard #Spring #SpringDataJPA #Projection