Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Правила описания Entity в JPA
Entity — это класс, который представляет таблицу в базе данных. Правильное описание Entity критически важно для корректной работы ORM и избежания проблем с производительностью и консистентностью данных.
Основные требования к Entity
1. Обязательная аннотация @Entity
@Entity
@Table(name = "users")
public class User {
// Класс должен быть помечен как Entity
}
2. Обязательный no-arg конструктор
JPA требует наличие конструктора без аргументов для создания экземпляров через рефлексию:
@Entity
public class User {
private Long id;
private String name;
// Требуется для JPA
public User() {
}
// Конструктор с аргументами для удобства
public User(Long id, String name) {
this.id = id;
this.name = name;
}
}
3. Не должен быть final
Entity не должен быть объявлен как final, так как JPA использует прокси для ленивой загрузки:
// ❌ Неправильно
@Entity
public final class User { }
// ✅ Правильно
@Entity
public class User { }
4. Методы не должны быть final
Это же касается методов — они не должны быть final для работы с прокси:
@Entity
public class User {
// ❌ Неправильно
public final void setName(String name) { }
// ✅ Правильно
public void setName(String name) { }
}
Определение первичного ключа
Простой первичный ключ
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
}
Стратегии генерации ключа
// AUTO — JPA выбирает оптимальную стратегию (по умолчанию)
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// IDENTITY — использует auto-increment БД
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// SEQUENCE — использует последовательность БД
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
@SequenceGenerator(name = "user_seq", sequenceName = "public.user_id_seq")
private Long id;
// TABLE — использует специальную таблицу для генерации
@GeneratedValue(strategy = GenerationType.TABLE, generator = "user_gen")
@TableGenerator(name = "user_gen", table = "sequences")
private Long id;
Составной первичный ключ (Composite Key)
@Embeddable
public class UserId implements Serializable {
private Long userId;
private String region;
public UserId() { }
public UserId(Long userId, String region) {
this.userId = userId;
this.region = region;
}
// equals и hashCode обязательны
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserId that = (UserId) o;
return Objects.equals(userId, that.userId) &&
Objects.equals(region, that.region);
}
@Override
public int hashCode() {
return Objects.hash(userId, region);
}
}
@Entity
public class UserRegion {
@EmbeddedId
private UserId id;
private String data;
}
Типы полей и маппинг
Базовые типы данных автоматически маппируются
@Entity
public class User {
@Id
private Long id;
private String name; // VARCHAR(255)
private int age; // INTEGER
private BigDecimal salary; // NUMERIC
private boolean active; // BOOLEAN
private LocalDate birthDate; // DATE
private LocalDateTime createdAt; // TIMESTAMP
}
Пользовательское маппинг колонки
@Entity
public class User {
@Column(name = "user_email", length = 100, nullable = false, unique = true)
private String email;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(columnDefinition = "VARCHAR(500) DEFAULT Active")
private String status;
}
Транзиентные поля
@Transient — поле не будет сохранено в БД:
@Entity
public class User {
@Id
private Long id;
private String firstName;
private String lastName;
@Transient
private String fullName;
@PostLoad
public void initFullName() {
this.fullName = firstName + " " + lastName;
}
}
Отношения между Entity
OneToMany и ManyToOne
@Entity
public class User {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Post> posts = new ArrayList<>();
}
@Entity
public class Post {
@Id
private Long id;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
private String title;
}
ManyToMany
@Entity
public class User {
@Id
private Long id;
@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
}
@Entity
public class Role {
@Id
private Long id;
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
}
OneToOne
@Entity
public class User {
@Id
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id", unique = true)
private UserProfile profile;
}
@Entity
public class UserProfile {
@Id
private Long id;
@OneToOne(mappedBy = "profile")
private User user;
}
Жизненный цикл Entity
Callback аннотации
@Entity
public class User {
@Id
private Long id;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
@PostLoad
protected void onLoad() {
// Логика после загрузки
}
@PreRemove
protected void onRemove() {
// Логика перед удалением
}
}
Наследование в Entity
Single Table Strategy — все классы в одной таблице:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "person_type")
public class Person {
@Id
private Long id;
private String name;
}
@Entity
@DiscriminatorValue("EMPLOYEE")
public class Employee extends Person {
private String jobTitle;
}
Важные практики
1. Используйте Serializable для составных ключей
@Embeddable
public class CompositeKey implements Serializable {
// ...
}
2. Переопределяйте equals() и hashCode() для правильной работы Set и Map
3. Избегайте циклических зависимостей в отношениях
4. Используйте @JsonIgnore для избежания циклов при сериализации
@Entity
public class User {
@OneToMany(mappedBy = "user")
@JsonIgnore
private List<Post> posts;
}
5. Не забывайте про fetch type при наличии больших коллекций
@OneToMany(fetch = FetchType.LAZY) // По умолчанию для OneToMany
private List<Post> posts;
@ManyToOne(fetch = FetchType.EAGER) // По умолчанию для ManyToOne
private User user;
Соблюдение этих правил обеспечивает надёжную работу ORM, правильную маппинг данных и оптимальную производительность приложения.