Istruzione INSERT in JPA

1. Panoramica

In questo rapido tutorial, impareremo come eseguire un'istruzione INSERT su oggetti JPA .

Per ulteriori informazioni su Hibernate in generale, consulta la nostra guida completa a JPA con Spring e l'introduzione a Spring Data con JPA per approfondimenti su questo argomento.

2. Oggetti persistenti in JPA

In JPA, ogni entità che passa da uno stato transitorio a uno stato gestito viene gestita automaticamente da EntityManager .

I EntityManager controlla se una data entità esiste già e poi decide se deve essere inserito o aggiornato. A causa di questa gestione automatica, le uniche istruzioni consentite da JPA sono SELECT, UPDATE e DELETE.

Negli esempi seguenti, esamineremo diversi modi per gestire e aggirare questa limitazione.

3. Definizione di un modello comune

Ora, iniziamo definendo una semplice entità che useremo in questo tutorial:

@Entity public class Person { @Id private Long id; private String firstName; private String lastName; // standard getters and setters, default and all-args constructors }

Inoltre, definiamo una classe di repository che useremo per le nostre implementazioni:

@Repository public class PersonInsertRepository { @PersistenceContext private EntityManager entityManager; }

Inoltre, applicheremo l' annotazione @Transactional per gestire automaticamente le transazioni entro Spring. In questo modo, non dovremo preoccuparci di creare transazioni con il nostro EntityManager, di confermare le nostre modifiche o di eseguire manualmente il rollback in caso di eccezione.

4. createNativeQuery

Per le query create manualmente, possiamo utilizzare il metodo EntityManager # createNativeQuery . Ci permette di creare qualsiasi tipo di query SQL, non solo quelle supportate da JPA. Aggiungiamo un nuovo metodo alla nostra classe di repository:

@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(); }

Con questo approccio, dobbiamo definire una query letterale che includa i nomi delle colonne e impostare i valori corrispondenti.

Ora possiamo testare il nostro repository:

@Test public void givenPersonEntity_whenInsertedTwiceWithNativeQuery_thenPersistenceExceptionExceptionIsThrown() { Person person = new Person(1L, "firstname", "lastname"); assertThatExceptionOfType(PersistenceException.class).isThrownBy(() -> { personInsertRepository.insertWithQuery(PERSON); personInsertRepository.insertWithQuery(PERSON); }); }

Nel nostro test, ogni operazione tenta di inserire una nuova voce nel nostro database. Poiché abbiamo provato a inserire due entità con lo stesso id , la seconda operazione di inserimento fallisce generando un'eccezione PersistenceException .

Il principio qui è lo stesso se usiamo @Query di Spring Data .

5. persistere

Nel nostro esempio precedente, abbiamo creato query di inserimento, ma abbiamo dovuto creare query letterali per ogni entità. Questo approccio non è molto efficiente e si traduce in un sacco di codice boilerplate.

Invece, possiamo utilizzare il metodo persist da EntityManager .

Come nel nostro esempio precedente, estendiamo la nostra classe di repository con un metodo personalizzato:

@Transactional public void insertWithEntityManager(Person person) { this.entityManager.persist(person); }

Ora possiamo testare di nuovo il nostro approccio :

@Test public void givenPersonEntity_whenInsertedTwiceWithEntityManager_thenEntityExistsExceptionIsThrown() { assertThatExceptionOfType(EntityExistsException.class).isThrownBy(() -> { personInsertRepository.insertWithEntityManager(new Person(1L, "firstname", "lastname")); personInsertRepository.insertWithEntityManager(new Person(1L, "firstname", "lastname")); }); }

A differenza dell'utilizzo di query native, non è necessario specificare i nomi delle colonne e i valori corrispondenti . Invece, EntityManager lo gestisce per noi.

Nel test precedente, ci aspettiamo anche che venga lanciata EntityExistsException invece della sua superclasse PersistenceException che è più specializzata e lanciata da persist .

D'altra parte, in questo esempio, dobbiamo assicurarci di chiamare il nostro metodo di inserimento ogni volta con una nuova istanza di Person. In caso contrario, sarà già gestito da EntityManager, con conseguente operazione di aggiornamento.

6. Conclusione

In questo articolo, abbiamo illustrato i modi per eseguire operazioni di inserimento su oggetti JPA. Abbiamo esaminato esempi di utilizzo di una query nativa, oltre all'utilizzo di EntityManager # persist per creare istruzioni INSERT personalizzate.

Come sempre, il codice completo utilizzato in questo articolo è disponibile su GitHub.