[Java] Как Spring Data Jdbc определяет, что объект новый
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В этом посте мы рассмотрим, как Spring Data Jdbc при сохранении объекта понимает: новая сущность и надо выполнить insert или такая сущность в базе данных уже есть и надо выполнить update.
Пост рассчитан на начинающих программистов и не содержит каких-то супер хитрых вещей.
Уже 13 ноября в OTUS пройдет demo-урок курса «Разработчик на Spring Framework» по теме: «Метрики и актуатор». По ссылке вы сможете бесплатно зарегистрироваться на урок. А прямо сейчас хочу поделиться с вами своей авторской статьей...
Прежде всего, определимся, почему задача определения «isNew» существует, откуда корни растут.
Допустим, у нас есть какой-то класс SomeObject. Объекты этого класса мы ходим сохранять в реляционной базе данных. Для этого мы создаем свой интерфейс:
@Repository
public 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, добавим в него несколько полей.
Одно из которых:
@Id
private 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.
Еще раз сформулируем работу этой стратегии.
- Берем значение поля Id
- Если null – объект новый
- Иначе, если не примитивный тип, значит – объект не новый.
- Если примитивный тип и значение 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
===========
Похожие новости:
- [Разработка веб-сайтов, Java] Разработка тем для портала Liferay 7
- [PHP, MySQL, CSS, JavaScript, HTML] RevolveR Contents Management Framework v.1.9.4.9
- [JavaScript, HTML, Accessibility] Решение проблемы обеспечения доступности модального окна для людей с ограниченными возможностями
- [Тестирование IT-систем, Java, Google Chrome, API] Замена UI авторизации на API для автотестов
- [Хостинг, WordPress, JavaScript] JAM-стэк — нищета на стероидах
- [DevOps, Kubernetes] Ansible с AWS и EC2 (перевод)
- [JavaScript, Программирование, Google Chrome, Управление медиа] Я никогда не писал расширения для Хрома, но меня допекли
- [Программирование, Scala] 5 уроков, которые я извлек для себя, продолжая осваивать ZIO (перевод)
- [Разработка мобильных приложений, Проектирование и рефакторинг, Разработка под Android] Это не я! История одного рефакторинга
- [Разработка веб-сайтов, JavaScript, Программирование] Примеры использования наблюдателей в JavaScript
Теги для поиска: #_java, #_java, #_spring_boot, #_spring, #_spring_data_jdbc, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
), #_java
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:20
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В этом посте мы рассмотрим, как Spring Data Jdbc при сохранении объекта понимает: новая сущность и надо выполнить insert или такая сущность в базе данных уже есть и надо выполнить update. Пост рассчитан на начинающих программистов и не содержит каких-то супер хитрых вещей. Уже 13 ноября в OTUS пройдет demo-урок курса «Разработчик на Spring Framework» по теме: «Метрики и актуатор». По ссылке вы сможете бесплатно зарегистрироваться на урок. А прямо сейчас хочу поделиться с вами своей авторской статьей... Прежде всего, определимся, почему задача определения «isNew» существует, откуда корни растут. Допустим, у нас есть какой-то класс SomeObject. Объекты этого класса мы ходим сохранять в реляционной базе данных. Для этого мы создаем свой интерфейс: @Repository
public interface RepositorySomeObject extends CrudRepository<SomeObject, Long> { } Теперь для сохранения объекта мы можем воспользоваться методом save интерфейса RepositorySomeObject. Разумеется, вновь возданный объект в базу данных попадет после выполнение Insert. Если же объект не новый, то надо выполнить update. Spring Data Jdbc должен как-то решить, когда выполнить insert, а когда update. Изучением того, как это делается мы и займемся. В официальной документации (раздел 9.6.8. Entity State Detection Strategies) сказано, что есть три стратегии определения новизны объекта:
Первые два используются сплошь и рядом, поэтому именно их мы и рассмотрим. Начнем с подготовки объекта. Создадим класс class SomeObject, добавим в него несколько полей. Одно из которых: @Id
private 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. Еще раз сформулируем работу этой стратегии.
Получается, все довольно просто и логично. У нового объекта идентификатора нет, поэтому он и новый. Значение ключевого поля формируется на стороне базы данных и возвращается вместе с сохраненным объектом. А что делать, если по каким-то причинам 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 =========== Похожие новости:
Блог компании OTUS. Онлайн-образование ), #_java |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:20
Часовой пояс: UTC + 5