1. Обзор
В этом кратком руководстве мы узнаем, как выполнить оператор INSERT для объектов JPA .
Для получения дополнительной информации о Hibernate в целом ознакомьтесь с нашим исчерпывающим руководством по JPA с Spring и введением в Spring Data с JPA для более глубокого погружения в эту тему.
2. Сохраняющиеся объекты в JPA
В JPA каждый объект, переходящий из переходного состояния в управляемое, автоматически обрабатывается EntityManager
.
EntityManager проверяет, существует
ли уже данный объект, а затем решает, следует ли его вставить или обновить. Из-за этого автоматического управления единственными операторами, разрешенными 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
для автоматической обработки транзакций с помощью Spring. Таким образом, нам не придется беспокоиться о создании транзакций с нашим EntityManager,
фиксации наших изменений или выполнении отката вручную в случае исключения.
4. создать нативный запрос
Для запросов, созданных вручную, мы можем использовать метод 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);
});
}
В нашем тесте каждая операция пытается вставить новую запись в нашу базу данных. Так как мы попытались вставить два объекта с одним и тем же идентификатором
, вторая операция вставки завершится ошибкой, создав исключение PersistenceException
.
Принцип здесь тот же, если мы используем Spring Data @Query .
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
.
С другой стороны, в этом примере мы должны убедиться, что мы вызываем наш метод вставки каждый раз с новым экземпляром Person.
В противном случае он уже будет управляться EntityManager,
что приведет к операции обновления.
6. Заключение
В этой статье мы проиллюстрировали способы выполнения операций вставки над объектами JPA. Мы рассмотрели примеры использования собственного запроса, а также использования EntityManager#persist
для создания пользовательских операторов INSERT.
Как всегда, полный код, использованный в этой статье, доступен на GitHub .