Русский
Русский
English
Статистика
Реклама

Как Spring Data Jdbc определяет, что объект новый

В этом посте мы рассмотрим, как Spring Data Jdbc при сохранении объекта понимает: новая сущность и надо выполнить insert или такая сущность в базе данных уже есть и надо выполнить update.
Пост рассчитан на начинающих программистов и не содержит каких-то супер хитрых вещей.


Уже 13 ноября в OTUS пройдет demo-урок курса Разработчик на Spring Framework по теме: Метрики и актуатор. По ссылке вы сможете бесплатно зарегистрироваться на урок. А прямо сейчас хочу поделиться с вами своей авторской статьей...

Прежде всего, определимся, почему задача определения isNew существует, откуда корни растут.
Допустим, у нас есть какой-то класс SomeObject. Объекты этого класса мы ходим сохранять в реляционной базе данных. Для этого мы создаем свой интерфейс:

@Repositorypublic interface RepositorySomeObject extends CrudRepository<SomeObject, Long> {}


Теперь для сохранения объекта мы можем воспользоваться методом save интерфейса RepositorySomeObject.

Разумеется, вновь возданный объект в базу данных попадет после выполнение Insert. Если же объект не новый, то надо выполнить update.
Spring Data Jdbc должен как-то решить, когда выполнить insert, а когда update.
Изучением того, как это делается мы и займемся.

В официальной документации (раздел 9.6.8. Entity State Detection Strategies) сказано, что есть три стратегии определения новизны объекта:

  • На основе поля Id
  • На основе имплементации интерфейса Persistable
  • На основе имплементации интерфейса EntityInformation

Первые два используются сплошь и рядом, поэтому именно их мы и рассмотрим.

Начнем с подготовки объекта.
Создадим класс class SomeObject, добавим в него несколько полей.
Одно из которых:

@Idprivate final Long id;

Обратите внимание на аннотацию Id, это важно.

Класс есть, интерфейс RepositorySomeObject мы тоже уже создали, можно сохранять в базу данных.
Для краткости, тут я не буду расписывать, как создается таблица, и в целом пропущу устройство проекта. По ссылке в конце статьи можно будет посмотреть все целиком.

Для изучения вопроса создадим пару тестов, и в них будем сохранять объект.
Для теста мы с помощью TestContainers поднимем Postgresql. На этом аспекте я тоже не буду останавливаться, можно будет все посмотреть в коде.

Тест у нас такой:
    @Test    void saveTestWithNullOrZeroId() {        var object = new SomeObject(null, "name", "value");        var savedObject = repository.save(object);        assertThat(savedObject).isNotNull();        assertThat(savedObject.getId()).isNotNull();    }

Новый объект сохраняем и проверяем, что Id заполнился.
Запускаем тест и смотрим, как Spring Data Jdbc понимает, что в данном случае нам надо выполнить именно insert, а не update.

Точка принятия решения находится в классе
org.springframework.data.jdbc.core.JdbcAggregateTemplateметод: public <T> T save(T instance)

вот фрагмент этого метода:
Function<T, MutableAggregateChange<T>> changeCreator = persistentEntity.isNew(instance) ? this::createInsertChange : this::createUpdateChange;return store(instance, changeCreator, persistentEntity);

Вся суть находится в persistentEntity.

Заглянем в persistentEntity.isNew, там увидим:
public boolean isNew(Object bean) {        this.verifyBeanType(bean);        return ((IsNewStrategy)this.isNewStrategy.get()).isNew(bean);}

Получается, что есть некая стратегия, которая и определяет, новый объект или нет.
Вопрос сводится к изучению, что это такое.

Обратимся к определению стратегии.
Это конструктор класса: org.springframework.data.mapping.model.
Вот фрагмент:
this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) ? PersistableIsNewStrategy.INSTANCE: getFallbackIsNewStrategy());

Что тут происходит?
Если сохраняемый объект имплементирует интерфейс PersistableIsNewStrategy, то используется соответствующая стратегия (об этом мы еще поговорим), если нет, то работает логика, представленная в методе getFallbackIsNewStrategy. Давайте на этом моменте остановимся подробнее.

Немного пройдем по цепочке вызовов getFallbackIsNewStrategy и окажемся в методе
public boolean isNew(Object entity) класса PersistentEntityIsNewStrategy.
В этом методе и определяется новый объект или нет.
Для этого берется значение поля, отмеченного аннотацией Id.
Если значение null значит объект новый.
Если не null, то возможно варианты.
Если это не примитивный тип данных, значит все понятно это объект не новый.
Если тип данных примитивный, то он по определению не может быть null, и выполняется проверка на 0.
Еще раз сформулируем работу этой стратегии.

  1. Берем значение поля Id
  2. Если null объект новый
  3. Иначе, если не примитивный тип, значит объект не новый.
  4. Если примитивный тип и значение 0, то новый, иначе не новый.

Получается, все довольно просто и логично. У нового объекта идентификатора нет, поэтому он и новый. Значение ключевого поля формируется на стороне базы данных и возвращается вместе с сохраненным объектом.

А что делать, если по каким-то причинам Id-шник надо сформировать в java-коде и передать в базу данных. В этом случае даже в новом объекте поле идентификатора будет заполнено и описанная выше стратегия уже не сработает.

Что делать в этой ситуации?
Использовать вторую стратегию, основанную на интерфейсе Persistable.
У этого интерфейса есть два метода getId и isNew.
Чтобы воспользоваться этим механизмом надо у объекта, который мы хотим сохранить, имплементировать этот интерфейс и самостоятельно определить, когда объект новый, а когда нет.
При выполнении кода:
this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) ? PersistableIsNewStrategy.INSTANCE: getFallbackIsNewStrategy());

Spring определит, что сохраняемый объект имплементирует интерфейс Persistable и вызовет метод isNew.

Подведем итоги.
Если идентификатор объекта формируется на стороне базы данных (наверное, самый частый случай), то достаточно на поле с идентификатором поставить аннотацию Id.
Если идентификатор создается на стороне приложения, то такой объект должен имплементировать интерфейс Persistable и реализовать метод isNew.

Полный пример находится по этой ссылке.
Видео-разбор можно посмотреть тут.
Источник: habr.com
К списку статей
Опубликовано: 02.11.2020 10:17:21
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus. онлайн-образование

Java

Spring boot

Spring

Spring data jdbc

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru