Spring Data JPA @Query

Panoramica

Spring Data fornisce molti modi per definire una query che possiamo eseguire. Uno di questi è l’annotazione @Query.

In questo tutorial, dimostreremo come utilizzare l’annotazione @Query in Spring Data JPA per eseguire sia query JPQL che SQL native.

Mostreremo anche come creare una query dinamica quando l’annotazione @ Query non è sufficiente.

Ulteriori letture:

Metodi di query derivati nei repository JPA di Spring Data

Esplora il meccanismo di derivazione delle query in Spring Data JPA.
Leggi di più →

Dati della Molla JPA @Modifica di Annotazione

Creare DML e DDL query in Primavera i Dati dell’APP, combinando Query @e @Modificare le annotazioni
Leggi di più →

Selezionare la Query

per definire SQL da eseguire per una Primavera di repository di Dati metodo, si può annotare il metodo con @Query annotazione — il suo valore attributo contiene il JPQL o SQL da eseguire.

L’annotazione @Query ha la precedenza sulle query nominate, che sono annotate con @ NamedQuery o definite in un orm.file xml.

È un buon approccio posizionare una definizione di query appena sopra il metodo all’interno del repository piuttosto che all’interno del nostro modello di dominio come query denominate. Il repository è responsabile della persistenza, quindi è un posto migliore per memorizzare queste definizioni.

2.1. JPQL

Per impostazione predefinita, la definizione della query utilizza JPQL.

Diamo un’occhiata a un semplice metodo di repository che restituisce entità utente attive dal database:

@Query("SELECT u FROM User u WHERE u.status = 1")Collection<User> findAllActiveUsers();

2.2. Nativo

Possiamo usare anche SQL nativo per definire la nostra query. Tutto quello che dobbiamo fare è impostare il valore dell’attributo nativeQuery su true e definire la query SQL nativa nell’attributo value dell’annotazione:

@Query( value = "SELECT * FROM USERS u WHERE u.status = 1", nativeQuery = true)Collection<User> findAllActiveUsersNative();

Definisci Ordine in una query

Possiamo passare un parametro aggiuntivo di tipo Sort a una dichiarazione del metodo Spring Data che ha l’annotazione @ Query. Verrà tradotto nella clausola ORDER BY che viene passata al database.

3.1. Ordinamento per JPA Fornito e Metodi Derivati

Per i metodi di get out of the box, come findAll(Ordinamento) o quelli che vengono generati mediante l’analisi del metodo di firme, si può solo utilizzare le proprietà dell’oggetto per definire il nostro ordinamento:

userRepository.findAll(Sort.by(Sort.Direction.ASC, "name"));

Ora immaginate che si desidera ordinare la lunghezza di un nome di proprietà:

userRepository.findAll(Sort.by("LENGTH(name)"));

Quando eseguiamo il codice di cui sopra, si riceverà un’eccezione:

org.quadro di primavera.dati.mappatura.PropertyReferenceException: Nessuna proprietà LENGTH (name) trovata per type User!

3.2. JPQL

Quando usiamo JPQL per la definizione di una query, poi Molla di Dati in grado di gestire l’ordinamento, senza alcun problema — tutto quello che dobbiamo fare è aggiungere il parametro di un metodo del tipo di Ordinamento:

@Query(value = "SELECT u FROM User u")List<User> findAllUsers(Sort sort);

Possiamo chiamare questo metodo e passare un parametro di Ordinamento, che ordina il risultato per la proprietà name dell’oggetto Utente:

userRepository.findAllUsers(Sort.by("name"));

E perché abbiamo usato @Query annotazione, possiamo usare lo stesso metodo per ottenere l’elenco ordinato degli Utenti per la lunghezza dei loro nomi:

userRepository.findAllUsers(JpaSort.unsafe("LENGTH(name)"));

È fondamentale che usiamo JpaSort.unsafe () per creare un’istanza dell’oggetto Sort.

Quando usiamo:

Sort.by("LENGTH(name)");

quindi riceveremo esattamente la stessa eccezione che abbiamo visto sopra per il metodo findAll ().

Quando Spring Data rileva l’ordinamento non sicuro per un metodo che utilizza l’annotazione @Query, aggiunge semplicemente la clausola di ordinamento alla query: salta il controllo se la proprietà da ordinare appartiene al modello di dominio.

3.3. Native

Quando l’annotazione @Query utilizza SQL nativo, non è possibile definire un Ordinamento.

Se lo facciamo, riceveremo un’eccezione:

org.quadro di primavera.dati.jpa.repository.query.InvalidJpaQueryMethodException: Impossibile utilizzare query native con ordinamento dinamico e / o impaginazione

Come dice l’eccezione, l’ordinamento non è supportato per le query native. Il messaggio di errore ci dà un suggerimento che l’impaginazione causerà anche un’eccezione.

Tuttavia, esiste una soluzione alternativa che consente l’impaginazione e la tratteremo nella prossima sezione.

Impaginazione

Impaginazione ci permette di restituire solo un sottoinsieme di un intero risultato in una pagina. Ciò è utile, ad esempio, quando si naviga tra diverse pagine di dati su una pagina Web.

Un altro vantaggio dell’impaginazione è che la quantità di dati inviati dal server al client è ridotta al minimo. Inviando piccoli pezzi di dati, possiamo generalmente vedere un miglioramento delle prestazioni.

4.1. JPQL

L’utilizzo dell’impaginazione nella definizione della query JPQL è semplice:

@Query(value = "SELECT u FROM User u ORDER BY id")Page<User> findAllUsersWithPagination(Pageable pageable);

Possiamo passare un parametro PageRequest per ottenere una pagina di dati.

L’impaginazione è supportata anche per le query native, ma richiede un po ‘ di lavoro aggiuntivo.

4.2. Native

Possiamo abilitare l’impaginazione per le query native dichiarando un attributo countQuery aggiuntivo.

Questo definisce l’SQL da eseguire per contare il numero di righe nell’intero risultato:

@Query( value = "SELECT * FROM Users ORDER BY id", countQuery = "SELECT count(*) FROM Users", nativeQuery = true)Page<User> findAllUsersWithPagination(Pageable pageable);

4.3. Spring Data JPA Versioni precedenti alla 2.0.4

La soluzione di cui sopra per le query native funziona bene per Spring Data JPA versioni 2.0.4 e successive.

Prima di quella versione, quando tentiamo di eseguire tale query, riceveremo la stessa eccezione che abbiamo descritto nella sezione precedente sull’ordinamento.

Possiamo superare questo problema aggiungendo un parametro aggiuntivo per l’impaginazione all’interno della nostra query:

@Query( value = "SELECT * FROM Users ORDER BY id \n-- #pageable\n", countQuery = "SELECT count(*) FROM Users", nativeQuery = true)Page<User> findAllUsersWithPagination(Pageable pageable);

Nell’esempio precedente, aggiungiamo “\ n– #pageable \ n ” come segnaposto per il parametro di impaginazione. Questo indica a Spring Data JPA come analizzare la query e iniettare il parametro pageable. Questa soluzione funziona per il database H2.

Abbiamo spiegato come creare semplici query select tramite JPQL e SQL nativo. Successivamente, mostreremo come definire parametri aggiuntivi.

Parametri di query indicizzati

Ci sono due modi possibili per passare i parametri del metodo alla nostra query: parametri indicizzati e denominati.

In questa sezione, tratteremo i parametri indicizzati.

5.1. JPQL

Per i parametri indicizzati in JPQL, Spring Data passerà i parametri del metodo alla query nello stesso ordine in cui appaiono nella dichiarazione del metodo:

@Query("SELECT u FROM User u WHERE u.status = ?1")User findUserByStatus(Integer status);@Query("SELECT u FROM User u WHERE u.status = ?1 and u.name = ?2")User findUserByStatusAndName(Integer status, String name);

Per le query di cui sopra, il parametro del metodo di stato verrà assegnato al parametro di query con indice 1 e il parametro del metodo nome verrà assegnato al parametro di query con indice 2.

5.2. Native

I parametri indicizzati per le query native funzionano esattamente come per JPQL:

@Query( value = "SELECT * FROM Users u WHERE u.status = ?1", nativeQuery = true)User findUserByStatusNative(Integer status);

Nella prossima sezione, mostreremo un approccio diverso: passare i parametri tramite il nome.

Parametri denominati

Possiamo anche passare i parametri del metodo alla query utilizzando i parametri denominati. Definiamo questi usando l’annotazione @ Param all’interno della nostra dichiarazione del metodo repository.

Ogni parametro annotato con @ Param deve avere una stringa di valore corrispondente al nome del parametro di query JPQL o SQL corrispondente. Una query con parametri denominati è più facile da leggere ed è meno soggetta a errori nel caso in cui la query debba essere refactored.

6.1. JPQL

Come accennato in precedenza, usiamo l’annotazione @ Param nella dichiarazione del metodo per abbinare i parametri definiti dal nome in JPQL con i parametri della dichiarazione del metodo:

@Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")User findUserByStatusAndNameNamedParams( @Param("status") Integer status, @Param("name") String name);

Si noti che nell’esempio precedente, abbiamo definito i parametri della query SQL e del metodo per avere gli stessi nomi, ma non è necessario purché le stringhe di valore siano le stesse:

@Query("SELECT u FROM User u WHERE u.status = :status and u.name = :name")User findUserByUserStatusAndUserName(@Param("status") Integer userStatus, @Param("name") String userName);

6.2. Native

Per la definizione della query nativa, non vi è alcuna differenza nel modo in cui passiamo un parametro tramite il nome alla query rispetto a JPQL-usiamo l’annotazione @ Param:

@Query(value = "SELECT * FROM Users u WHERE u.status = :status and u.name = :name", nativeQuery = true)User findUserByStatusAndNameNamedParamsNative( @Param("status") Integer status, @Param("name") String name);

7. Parametro Collection

Consideriamo il caso in cui la clausola where della nostra query JPQL o SQL contiene la parola chiave IN (o NOT IN) :

SELECT u FROM User u WHERE u.name IN :names

In questo caso, possiamo definire un metodo di query che prende la raccolta come parametro:

@Query(value = "SELECT u FROM User u WHERE u.name IN :names")List<User> findUserByNameList(@Param("names") Collection<String> names);

Poiché il parametro è una raccolta, può essere utilizzato con List, HashSet,ecc.

Successivamente, mostreremo come modificare i dati con l’annotazione @ Modifying.

Aggiorna le query con @Modifying

Possiamo usare l’annotazione @Query per modificare lo stato del database aggiungendo anche l’annotazione @Modifying al metodo repository.

8.1. JPQL

Il metodo repository che modifica i dati ha due differenze rispetto alla query select: ha l’annotazione @ Modifying e, naturalmente, la query JPQL utilizza update invece di select:

@Modifying@Query("update User u set u.status = :status where u.name = :name")int updateUserSetStatusForName(@Param("status") Integer status, @Param("name") String name);

Il valore restituito definisce il numero di righe aggiornate dall’esecuzione della query. Sia i parametri indicizzati che quelli denominati possono essere utilizzati all’interno delle query di aggiornamento.

8.2. Native

Possiamo modificare lo stato del database anche con una query nativa. Abbiamo solo bisogno di aggiungere l’annotazione @ Modifying:

@Modifying@Query(value = "update Users u set u.status = ? where u.name = ?", nativeQuery = true)int updateUserSetStatusForNameNative(Integer status, String name);

8.3. Inserts

Per eseguire un’operazione di inserimento, dobbiamo applicare @ Modifying e utilizzare una query nativa poiché INSERT non fa parte dell’interfaccia JPA:

@Modifying@Query( value = "insert into Users (name, age, email, status) values (:name, :age, :email, :status)", nativeQuery = true)void insertUser(@Param("name") String name, @Param("age") Integer age, @Param("status") Integer status, @Param("email") String email);

Query dinamica

Spesso, incontreremo la necessità di creare istruzioni SQL basate su condizioni o set di dati i cui valori sono noti solo in fase di runtime. E in questi casi, non possiamo semplicemente usare una query statica.

9.1. Esempio di una query dinamica

Ad esempio, immaginiamo una situazione in cui dobbiamo selezionare tutti gli utenti la cui email è simile a una da un set definito in fase di runtime-email1, email2—…, emailn:

SELECT u FROM User u WHERE u.email LIKE '%email1%' or u.email LIKE '%email2%' ... or u.email LIKE '%emailn%'

Poiché il set è costruito dinamicamente, non possiamo sapere in fase di compilazione quante clausole SIMILI aggiungere.

In questo caso, non possiamo semplicemente utilizzare l’annotazione @Query poiché non possiamo fornire un’istruzione SQL statica.

Invece, implementando un repository composito personalizzato, possiamo estendere la funzionalità JpaRepository di base e fornire la nostra logica per la creazione di una query dinamica. Diamo un’occhiata a come fare questo.

9.2. Repository personalizzati e l’API JPA Criteria

Fortunatamente per noi, Spring fornisce un modo per estendere il repository di base attraverso l’uso di interfacce di frammenti personalizzate. Possiamo quindi collegarli insieme per creare un repository composito.

Inizieremo con la creazione di un’interfaccia frammento personalizzato:

public interface UserRepositoryCustom { List<User> findUserByEmails(Set<String> emails);}

E poi lo implementeremo:

public class UserRepositoryCustomImpl implements UserRepositoryCustom { @PersistenceContext private EntityManager entityManager; @Override public List<User> findUserByEmails(Set<String> emails) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<User> query = cb.createQuery(User.class); Root<User> user = query.from(User.class); Path<String> emailPath = user.get("email"); List<Predicate> predicates = new ArrayList<>(); for (String email : emails) { predicates.add(cb.like(emailPath, email)); } query.select(user) .where(cb.or(predicates.toArray(new Predicate))); return entityManager.createQuery(query) .getResultList(); }}

Come mostrato sopra, abbiamo sfruttato l’API JPA Criteria per costruire la nostra query dinamica.

Inoltre, dobbiamo assicurarci di includere il postfix Impl nel nome della classe. Spring cercherà l’implementazione UserRepositoryCustom come UserRepositoryCustomImpl. Poiché i frammenti non sono repository da soli, Spring si basa su questo meccanismo per trovare l’implementazione del frammento.

9.3. Estensione del repository esistente

Si noti che tutti i metodi di query dalla sezione 2 alla sezione 7 sono in UserRepository.

Quindi, ora integreremo il nostro frammento estendendo la nuova interfaccia nel UserRepository:

public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom { // query methods from section 2 - section 7}

9.4. Usando il Repository

E infine, possiamo chiamare il nostro metodo di query dinamica:

Set<String> emails = new HashSet<>();// filling the set with any number of itemsuserRepository.findUserByEmails(emails);

Abbiamo creato con successo un repository composito e chiamato il nostro metodo personalizzato.

Conclusione

In questo articolo, abbiamo trattato diversi modi di definire le query nei metodi di repository JPA di Spring Data utilizzando l’annotazione @Query.

Abbiamo anche imparato come implementare un repository personalizzato e creare una query dinamica.

Come sempre, gli esempi completi di codice utilizzati in questo articolo sono disponibili su GitHub.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.