1. Обзор
В этом кратком руководстве мы узнаем, как выполнить инструкцию INSERT для объектов JPA .
Для получения дополнительной информации о спящем режиме в целом ознакомьтесь с нашим всеобъемлющим руководством по JPA с Spring и введением в данные Spring с JPA для глубокого погружения в эту тему.
2. Сохраняющиеся объекты в JPA
В JPA каждый объект, переходящий из переходного состояния в управляемое, автоматически обрабатывается EntityManager .
EntityManager проверяет, существует ли уже данный объект, а затем решает, следует ли его вставлять или обновлять. Из-за этого автоматического управления t he только операторы, разрешенные JPA, являются SELECT, UPDATE и DELETE.
В приведенных ниже примерах мы рассмотрим различные способы управления и обхода этого ограничения.
3. Определение общей модели
Теперь давайте начнем с определения простой сущности, которую мы будем использовать на протяжении всего этого урока:
@Entity public class Person { @Id private Long id; private String firstName; private String lastName; // standard getters and setters, default and all-args constructors }
Кроме того, давайте определим класс репозитория, который мы будем использовать для наших реализаций:
@Repository public class PersonInsertRepository { @PersistenceContext private EntityManager entityManager; }
Кроме того, мы применим аннотацию @Transactional для автоматической обработки транзакций к весне. Таким образом, нам не придется беспокоиться о создании транзакций с помощью нашего EntityManager, фиксации наших изменений или выполнении отката вручную в случае исключения.
4. createNativeQuery
Для запросов, созданных вручную, мы можем использовать метод EntityManager#createNativeQuery . Это позволяет нам создавать любые типы SQL-запросов, а не только те, которые поддерживаются JPA. Давайте добавим новый метод в наш класс репозитория:
@Transactional public void insertWithQuery(Person person) { entityManager.createNativeQuery("INSERT INTO person (id, first_name, last_name) VALUES (?,?,?)") .setParameter(1, person.getId()) .setParameter(2, person.getFirstName()) .setParameter(3, person.getLastName()) .executeUpdate(); }
При таком подходе нам нужно определить литеральный запрос, включающий имена столбцов, и установить их соответствующие значения.
Теперь мы можем протестировать наш репозиторий:
@Test public void givenPersonEntity_whenInsertedTwiceWithNativeQuery_thenPersistenceExceptionExceptionIsThrown() { Person person = new Person(1L, "firstname", "lastname"); assertThatExceptionOfType(PersistenceException.class).isThrownBy(() -> { personInsertRepository.insertWithQuery(PERSON); personInsertRepository.insertWithQuery(PERSON); }); }
В нашем тесте каждая операция пытается вставить новую запись в нашу базу данных. Поскольку мы попытались вставить два объекта с одинаковым id , вторая операция вставки завершается неудачей, вызвав исключение PersistenceException .
Принцип здесь тот же, если мы используем @Query Spring Data .
5. упорствовать
В нашем предыдущем примере мы создали запросы вставки, но нам пришлось создавать литеральные запросы для каждой сущности. Этот подход не очень эффективен и приводит к большому количеству шаблонного кода.
Вместо этого мы можем использовать метод persist из EntityManager .
Как и в нашем предыдущем примере, давайте расширим наш класс репозитория с помощью пользовательского метода:
@Transactional public void insertWithEntityManager(Person person) { this.entityManager.persist(person); }
Теперь мы можем еще раз проверить наш подход :
@Test public void givenPersonEntity_whenInsertedTwiceWithEntityManager_thenEntityExistsExceptionIsThrown() { assertThatExceptionOfType(EntityExistsException.class).isThrownBy(() -> { personInsertRepository.insertWithEntityManager(new Person(1L, "firstname", "lastname")); personInsertRepository.insertWithEntityManager(new Person(1L, "firstname", "lastname")); }); }
В отличие от использования собственных запросов, нам не нужно указывать имена столбцов и соответствующие значения . Вместо этого EntityManager обрабатывает это для нас.
В приведенном выше тесте мы также ожидаем, что EntityExistsException будет выброшен вместо его суперкласса PersistenceException , который является более специализированным и выбрасывается persist/|.
С другой стороны, в этом примере мы должны убедиться, что мы вызываем наш метод insert каждый раз с новым экземпляром Person. В противном случае он уже будет управляться EntityManager, что приведет к операции обновления.
6. Заключение
В этой статье мы проиллюстрировали способы выполнения операций вставки для объектов JPA. Мы рассмотрели примеры использования собственного запроса, а также использования EntityManager#persist для создания пользовательских операторов ВСТАВКИ.
Как всегда, полный код, используемый в этой статье, доступен на GitHub .