Что такое ORM
ORM (Object-Relational Mapping) — это техника, которая позволяет работать с реляционными базами данных, используя объектно-ориентированный подход. Hibernate — самая популярная реализация JPA (Java Persistence API) в экосистеме Java.
▸Преимущества ORM
Сущности и аннотации
▸Базовая сущность
1@Entity2@Table(name = "users")3@Getter @Setter @NoArgsConstructor4public class User {56 @Id7 @GeneratedValue(strategy = GenerationType.IDENTITY)8 private Long id;910 @Column(nullable = false, length = 100)11 private String name;1213 @Column(unique = true, nullable = false)14 private String email;1516 @Enumerated(EnumType.STRING)17 @Column(nullable = false)18 private UserStatus status;1920 @CreationTimestamp21 private LocalDateTime createdAt;2223 @UpdateTimestamp24 private LocalDateTime updatedAt;2526 @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)27 private List<Order> orders = new ArrayList<>();2829 @ManyToOne(fetch = FetchType.LAZY)30 @JoinColumn(name = "department_id")31 private Department department;32}
Типы связей
▸One-to-One
1@Entity2public class UserProfile {3 @OneToOne(fetch = FetchType.LAZY)4 @JoinColumn(name = "user_id")5 private User user;67 @OneToOne(mappedBy = "profile", cascade = CascadeType.ALL)8 private Address address;9}
▸One-to-Many / Many-to-One
1@Entity2public class Order {3 @ManyToOne(fetch = FetchType.LAZY)4 @JoinColumn(name = "user_id", nullable = false)5 private User user;67 @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)8 private List<OrderItem> items = new ArrayList<>();9}
▸Many-to-Many
1@Entity2public class Student {3 @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})4 @JoinTable(5 name = "student_course",6 joinColumns = @JoinColumn(name = "student_id"),7 inverseJoinColumns = @JoinColumn(name = "course_id")8 )9 private Set<Course> courses = new HashSet<>();10}
Стратегии загрузки
▸Eager vs Lazy
▸Проблема N+1
Одна из самых частых проблем производительности:
1// N+1 проблема: 1 запрос на пользователей + N запросов на заказы2List<User> users = userRepository.findAll();3users.forEach(u -> u.getOrders().size()); // LazyInitializationException
▸Решение: JOIN FETCH
1@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")2User findUserWithOrders(@Param("id") Long id);
Запросы
▸JPQL (Java Persistence Query Language)
1// JPQL запрос2@Query("SELECT u FROM User u WHERE u.status = :status AND u.createdAt > :date")3List<User> findActiveUsersAfter(@Param("status") UserStatus status,4 @Param("date") LocalDateTime date);56// Native SQL запрос7@Query(value = "SELECT * FROM users u WHERE u.email LIKE %:domain%",8 nativeQuery = true)9List<User> findByEmailDomain(@Param("domain") String domain);
▸Criteria API
1CriteriaBuilder cb = entityManager.getCriteriaBuilder();2CriteriaQuery<User> query = cb.createQuery(User.class);3Root<User> user = query.from(User.class);45query.select(user)6 .where(7 cb.equal(user.get("status"), UserStatus.ACTIVE),8 cb.greaterThan(user.get("createdAt"), LocalDateTime.now().minusDays(30))9 )10 .orderBy(cb.desc(user.get("createdAt")));1112List<User> results = entityManager.createQuery(query).getResultList();
Кэширование
▸Уровни кэша Hibernate
L1 (Session Cache) — кэш текущей сессии
L2 (Second Level Cache) — кэш между сессиями
Query Cache — кэш результатов запросов
▸Настройка L2 кэша
1@Entity2@Cacheable3@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)4public class User {5 // ...6}
Оптимизация производительности
▸Batch Fetching
1@OneToMany(mappedBy = "user")2@BatchSize(size = 25)3private List<Order> orders;
▸Проекция
1// Interface-based projection2public interface UserSummary {3 String getName();4 String getEmail();5}67@Query("SELECT u.name as name, u.email as email FROM User u")8List<UserSummary> findAllSummaries();
Транзакции
▸Управление транзакциями
1@Service2public class OrderService {34 @Transactional5 public Order createOrder(CreateOrderRequest request) {6 User user = userRepository.findById(request.getUserId())7 .orElseThrow(() -> new RuntimeException("User not found"));89 Order order = new Order(user, request.getItems());10 return orderRepository.save(order);11 }1213 @Transactional(readOnly = true)14 public Order getOrder(Long id) {15 return orderRepository.findById(id)16 .orElseThrow(() -> new RuntimeException("Order not found"));17 }18}
Заключение
Hibernate и JPA — критически важные темы для собеседования на Java-разработчика. Понимание стратегий загрузки, проблем производительности (N+1), кэширования и транзакций демонстрирует глубокие знания ORM. Практикуйтесь с реальными запросами и следите за SQL, генерируемым Hibernate.