JavaRush/Курсы/Модуль 5. Spring/Работа с репозиториями: CrudRepository, JpaRepository

Работа с репозиториями: CrudRepository, JpaRepository

Открыта

Добро пожаловать в одну из самых полезных и популярных тем Spring Data — работу с репозиториями! В этой лекции мы разберем, что такое репозитории, зачем они нужны, чем отличаются CrudRepository и JpaRepository, как они упрощают нашу жизнь, и, конечно же, внедрим их в наше приложение. Все это будет полезно не только в ваших учебных проектах, но и в реальной работе, ведь репозитории — это основа взаимодействия с базой данных в Spring.


Что такое репозиторий?

Репозиторий (Repository) в данном контексте — это слой приложения, который отвечает за взаимодействие с базой данных. Он абстрагирует базовые операции вроде создания, чтения, обновления и удаления данных (так называемые CRUD-операции).

Если раньше взаимодействие с базой данных требовало ручного написания SQL-запросов, то Spring Data упрощает этот процесс в разы. Вам больше не нужно "пачкать руки" SQL'ем (хотя никто не запрещает, если хочется). Вместо этого репозитории позволяют фокусироваться на бизнес-логике, доверив рутинные задачи Spring.


CrudRepository и JpaRepository: отличия

Spring Data предоставляет несколько интерфейсов для работы с базой данных, но два из них используются наиболее часто:

CrudRepository

CrudRepository — это базовый интерфейс для выполнения CRUD-операций. Вот его основные методы:

public interface CrudRepository<T, ID> {
<S extends T> S save(S entity);           // Сохранение (или обновление) сущности
  Optional<T> findById(ID id);             // Поиск сущности по идентификатору
    boolean existsById(ID id);               // Проверка на существование записи
    Iterable<T> findAll();                   // Получение всех сущностей
      void deleteById(ID id);                  // Удаление по идентификатору
      void delete(T entity);                   // Удаление переданной сущности
}

Когда использовать: если вам нужно реализовать только базовые CRUD-операции.

JpaRepository

JpaRepository расширяет CrudRepository и добавляет новые возможности:

  • Пагинация (разбивка большого списка данных на страницы, как в Google: 10 результатов на странице)
  • Сортировка данных
  • JPA-специфичные методы вроде flush() для управления состоянием базы

Пример пагинации:

// Получить первые 20 пользователей, отсортированных по имени
Page<User> users = repository.findAll(PageRequest.of(0, 20, Sort.by("name")));

Примеры методов из JpaRepository:

public interface JpaRepository<T, ID> extends CrudRepository<T, ID> {
List<T> findAll(Sort sort);          // Сортировка результатов
  Page<T> findAll(Pageable pageable);  // Пагинация результатов
    void flush();                        // Сохранение изменений в базу данных
    <S extends T> S saveAndFlush(S entity); // Сохранить и сразу сбросить контекст
}

Когда использовать: если вам нужны дополнительные возможности DAO-уровня, такие как сортировка или пагинация.


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

Теперь, когда теория стала немного понятнее, перейдем к практике! Мы будем работать с приложением, где уже есть сущность User.

1. Сущность User

Наш класс User — это простейшая сущность, которая будет хранить данные о пользователе:

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    public User() {}

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // Геттеры и сеттеры
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

2. Создание репозитория

Теперь создадим интерфейс репозитория. Spring Data сделает всё остальное за нас:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    // Здесь можно добавлять кастомные методы
}

Вот и всё! Репозиторий готов. Теперь у нас есть доступ ко всем CRUD-операциям, которые предоставляет JpaRepository.


Выполнение CRUD-операций

Попробуем использовать наш репозиторий для базовых операций.

Сохранение пользователя

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(String name, String email) {
        User user = new User(name, email);
        return userRepository.save(user);
    }
}

Получение пользователя по ID

public Optional<User> getUserById(Long id) {
    return userRepository.findById(id);
}

Обновление данных пользователя

public User updateUser(Long id, String name, String email) {
    User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
    user.setName(name);
    user.setEmail(email);
    return userRepository.save(user);
}

Удаление пользователя

public void deleteUser(Long id) {
    userRepository.deleteById(id);
}

Использование в REST-контроллере Создадим контроллер, который будет использовать наш UserService:

@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userService.createUser(user.getName(), user.getEmail());
        return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.getUserById(id)
            .map(user -> new ResponseEntity<>(user, HttpStatus.OK))
            .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        User updatedUser = userService.updateUser(id, user.getName(), user.getEmail());
        return new ResponseEntity<>(updatedUser, HttpStatus.OK);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
}

Кастомные методы репозиториев

Иногда стандартных операций недостаточно, и нужно написать что-то своё. Например, поиск пользователя по имени.

Реализация с использованием имени метода

List<User> findByName(String name);

Spring Data "магически" понимает, что вы хотите найти пользователей по имени, благодаря соглашениям об именовании методов.

Реализация с использованием @Query

@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

Пагинация Если у вас есть много данных, вы можете возвращать их небольшими частями:

Page<User> findAll(Pageable pageable);

Использование:

Pageable pageable = PageRequest.of(0, 10, Sort.by("name"));
Page<User> page = userRepository.findAll(pageable);

Возможные ошибки и как их избежать

Вот несколько распространённых ошибок, о которых следует помнить:

  • Ошибка "No EntityManager with actual transaction": не забудьте добавить аннотацию @Transactional в сервисные методы, которые изменяют данные.
  • Отсутствие конструктора без параметров в сущности: убедитесь, что у классов-сущностей есть публичный конструктор без параметров.
  • Ошибка с ленивой загрузкой данных (LazyInitializationException): будьте осторожны с ленивой загрузкой и старайтесь использовать FetchType.LAZY только там, где это необходимо.

Зачем всё это нужно?

Репозитории — это великолепный способ сосредоточиться на логике приложения, не тратя время на рутину. Работать с репозиториями быстрее, безопаснее и намного проще, чем писать SQL-запросы вручную.

В реальной работе вы будете постоянно использовать репозитории, хоть в простом CRUD-приложении, хоть в сложной микросервисной системе с десятками баз данных.

Комментарии
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
У этой страницы еще нет ни одного комментария