Логотип Workflow

Article

Updated at:

Crud Operations

Этап 4 - CRUD операции: Create, Read, Update, Delete

CRUD - это не просто четыре repository calls, а жизненный цикл данных вместе с validation, errors и transactions. Большинство бизнес-функций соединяют Create, Read, Update и Delete с validation, authorization, transactions и domain rules. В Spring Data JPA repositories дают низкоуровневые CRUD methods, а services решают, как использовать их безопасно.

CRUD service flow

Create и Read

Create означает вставку новой записи. В Java-коде обычно создают entity object, заполняют обязательные поля, проверяют бизнес-правила и вызывают save. Hibernate генерирует insert, когда у объекта еще нет database identity.

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

Read означает загрузку данных. Чтение по id использует findById. Чтение списка может использовать findAll, но production API обычно должны применять pagination. Pageable описывает номер страницы, размер страницы и сортировку. Page<T> содержит данные и metadata: total elements, total pages и другую информацию.

public Page<Product> listProducts(Pageable pageable) {
    return productRepository.findAll(pageable);
}

Update

Update обычно должен начинаться с загрузки существующей entity. Это важно, потому что строки в базе может не быть, а текущее состояние может понадобиться для validation. После загрузки entity внутри transaction меняют ее поля. Hibernate отслеживает managed entities и отправляет изменения при commit.

@Transactional
public User renameUser(Long id, String name) {
    User user = userRepository.findById(id)
        .orElseThrow(() -> new EntityNotFoundException("User not found: " + id));
    user.setName(name);
    return user;
}

Вызывать save после изменения managed entity часто не обязательно, но многие команды делают это ради явности. Главное - не обновлять данные через создание нового объекта только с id и несколькими полями, если ты не понимаешь merge behavior. Такой подход может случайно перезаписать поля значением null.

Delete и service layer

Delete удаляет данные. deleteById краткий, но для бизнес-операций часто лучше сначала загрузить entity, проверить правила и затем вызвать delete. Например, удаление товара может быть запрещено, если по нему есть завершенные заказы. Такие правила относятся к service layer, а не к controller.

Controllers должны преобразовывать HTTP input и output. Services должны координировать use cases. Repositories должны сохранять entities. Если CRUD-код находится прямо в controllers, error handling, validation и transactions расползаются по приложению.

CRUD-сценарийПравильный service flowЧастая ошибка новичка
Создать пользователяПроверить request, создать новую entity, задать defaults, вызвать save.Доверять полям request напрямую и забыть обязательные defaults вроде createdAt.
Прочитать одного пользователяВызвать findById, превратить empty Optional в domain error или HTTP 404.Вызвать .get() и получить неясную runtime exception.
Получить список товаровПринять Pageable, выбрать sorting, вернуть DTO page.Вызвать findAll() и загрузить всю таблицу ради одного экрана.
Обновить цену товараЗагрузить существующую entity, проверить правила, изменить поля внутри transaction.Создать новую partial entity и случайно перезаписать поля значением null.
Удалить записьЗагрузить entity, проверить, что удаление разрешено, затем удалить.Удалять по id без проверки ownership, status или dependent records.

Обработка ошибок

EntityNotFoundException или custom exception делает отсутствие данных явным. В REST API exception можно связать с HTTP 404 через @ControllerAdvice. Custom exceptions часто лучше, когда важен язык предметной области: UserNotFoundException или ProductNotFoundException.

Ошибки нельзя молча проглатывать. Если цель update не существует, API не должен тихо создавать другую строку, если feature явно не поддерживает upsert. Понятные ошибки упрощают debugging клиентов и предотвращают скрытую порчу данных.

Практика

Реализуй CRUD для пользователей и товаров. Для пользователей создай endpoints: create, get by id, list with pagination, rename и delete. Для товаров добавь обновление цены и availability. Оставь controllers тонкими: они вызывают services и возвращают DTOs. Поставь @Transactional на service methods, которые изменяют данные. Проверь missing ids и убедись, что они дают предсказуемый error response.

Чек-лист понимания

  • Могу объяснить Create, Read, Update и Delete в терминах JPA.
  • Понимаю, почему update обычно начинается с findById.
  • Понимаю Pageable и Page.
  • Знаю, почему controllers не должны содержать CRUD business logic.
  • Могу спроектировать понятный not-found flow.

Авторизуйтесь чтоб пройти тесты

Practice

Интерактивная практика

Выполните задания и сразу проверьте ответ.