← Назад к вопросам
Какая аннотация запрещает обновление поля сущности?
1.0 Junior🔥 201 комментариев
#ORM и Hibernate
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Аннотация для запрета обновления поля сущности
Для запрета обновления (update) поля в JPA сущности используется аннотация @Column(updatable = false). Это одна из важных аннотаций при работе с базой данных.
@Column(updatable = false)
Эта аннотация указывает JPA, что поле не должно быть обновляемо в SQL UPDATE запросах:
import javax.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Поле можно вставить один раз (INSERT), но не обновить (UPDATE)
@Column(updatable = false)
private String email;
// Обычное поле, которое можно обновлять
@Column
private String name;
// Поле создания, часто используется с updatable = false
@Column(updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
// Поле обновления, только для чтения при INSERT
@Column(insertable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
}
Практический пример
@Entity
@Table(name = "users")
public class User {
@Id
private Long id;
// Email не может быть изменён после создания
@Column(updatable = false, nullable = false)
private String email;
// Имя можно менять
private String name;
// Дата создания устанавливается один раз
@Column(updatable = false)
@CreationTimestamp
private LocalDateTime createdAt;
// Дата последнего обновления автоматически обновляется
@UpdateTimestamp
private LocalDateTime updatedAt;
// Версия для оптимистичной блокировки
@Version
private Long version;
}
Как это работает
Попытка обновления
// Сохранение нового пользователя
User user = new User();
user.setEmail("john@example.com"); // OK - INSERT
user.setName("John");
userRepository.save(user);
// Позже попытка обновить email
User existing = userRepository.findById(1L).orElseThrow();
existing.setEmail("newemail@example.com"); // Вызов работает
existing.setName("Johnny"); // Работает нормально
userRepository.save(existing); // SAVE работает, но email НЕ обновится в БД!
// Проверка
User reloaded = userRepository.findById(1L).orElseThrow();
System.out.println(reloaded.getEmail()); // Остался "john@example.com"
System.out.println(reloaded.getName()); // "Johnny" - обновился
Сгенерированный SQL
-- Первое сохранение (INSERT)
INSERT INTO users (email, name, created_at)
VALUES ('john@example.com', 'John', NOW());
-- Попытка обновления
-- updatable = false означает, что email НЕ будет включён в UPDATE
UPDATE users SET name = 'Johnny', updated_at = NOW()
WHERE id = 1;
-- Обратите внимание: email НЕ в SET части!
Паттерны использования
1. Immutable поля (ID, создание, версия)
@Entity
public class BlogPost {
@Id
@GeneratedValue
private Long id;
@Column(updatable = false)
private String slug; // URL-friendly identifier, не меняется
@Column(updatable = false)
private LocalDateTime publishedAt; // Дата публикации
private String title; // Можно редактировать
private String content; // Можно редактировать
}
2. Аудит полей
@Entity
public class Document {
@Id
private Long id;
@Column(updatable = false)
private String createdBy; // Кто создал
@Column(updatable = false)
private LocalDateTime createdAt; // Когда создал
private String content;
private String modifiedBy; // Кто последний раз редактировал
private LocalDateTime modifiedAt; // Когда
}
3. Системные идентификаторы
@Entity
public class Account {
@Id
private String accountNumber; // IBAN или номер счёта
@Column(updatable = false)
private String accountNumber; // Уникальный идентификатор
@Column(updatable = false)
private String accountType; // Тип счёта, не меняется
private BigDecimal balance; // Может меняться
}
Комбинация с insertable
@Entity
public class Entity {
@Id
private Long id;
// Только для вставки, не обновляется
@Column(insertable = true, updatable = false)
private String createdBy;
// Для вставки и обновления
@Column(insertable = true, updatable = true)
private String modifiedBy;
// Ни для вставки, ни для обновления (вычисляемое поле)
@Column(insertable = false, updatable = false)
private String computedValue;
}
Важные замечания
Hibernate vs Spring Data JPA
Все JPA реализации (Hibernate, EclipseLink) поддерживают updatable = false.
Проверка на уровне БД
-- Лучше всего добавить ограничение на уровне БД
CREATE TABLE users (
id BIGINT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
name VARCHAR(255),
created_at TIMESTAMP NOT NULL,
CONSTRAINT chk_email_immutable CHECK (email IS NOT NULL)
);
Безопасность
// updatable = false это не замена валидации!
// Это просто указание ORM как генерировать SQL
// Плохо: только на уровне ORM
@Column(updatable = false)
private String email;
// Хорошо: валидация + ORM
@Column(updatable = false, nullable = false)
@Email
private String email;
Тестирование
@SpringBootTest
public class UserEntityTest {
@Autowired
private UserRepository repo;
@Test
public void emailShouldNotUpdate() {
// Создание
User user = new User();
user.setEmail("original@example.com");
user.setName("John");
User saved = repo.save(user);
// Обновление
saved.setEmail("changed@example.com");
saved.setName("Johnny");
repo.save(saved);
// Проверка
User reloaded = repo.findById(saved.getId()).orElseThrow();
assertEquals("original@example.com", reloaded.getEmail()); // Не изменился
assertEquals("Johnny", reloaded.getName()); // Изменился
}
}
Альтернативы
1. @Immutable (для целого класса)
import org.hibernate.annotations.Immutable;
@Entity
@Immutable
public class ReadOnlyEntity {
// Всё поле не обновляется
}
2. @CreationTimestamp и @UpdateTimestamp (Hibernate)
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
@Entity
public class AuditedEntity {
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
Выводы
- @Column(updatable = false) — запрещает обновление поля в UPDATE запросах
- Используется для: ID, дат создания, неизменяемых идентификаторов
- Это не замена безопасности: используй валидацию и ограничения БД
- Работает со всеми JPA реализациями
- На уровне приложения: ORM просто исключит поле из UPDATE SQL