Spring Data JPA @Query

overzicht

Spring Data biedt vele manieren om een query te definiëren die we kunnen uitvoeren. Een van deze is de @Query annotatie.

in deze tutorial zullen we laten zien hoe de @Query annotatie in Spring Data JPA gebruikt wordt om zowel JPQL als native SQL query ‘ s uit te voeren.

we zullen ook laten zien hoe je een dynamische query bouwt als de @ Query-annotatie niet genoeg is.

verder lezen:

afgeleide Query methoden in voorjaar data JPA Repositories

verkennen van de query afleiding mechanisme in voorjaar data JPA.
Lees meer →

Spring Data JPA @Modifying Annotation

maak DML-en DDL-query ‘ s in Spring Data JPA door de @Query en @Modifying annotations
te combineren Lees meer →

Selecteer Query

om SQL te definiëren om uit te voeren voor een Spring Data repository methode, kunnen we de methode annoteren met de @Query annotatie — het waarde attribuut bevat de JPQL of SQL om uit te voeren.

de @ Query-annotatie heeft voorrang op benoemde query ‘ s, die zijn geannoteerd met @NamedQuery of gedefinieerd in een orm.xml-bestand.

het is een goede benadering om een query definitie net boven de methode in de repository te plaatsen in plaats van in ons domein model als benoemde queries. De repository is verantwoordelijk voor persistentie, dus het is een betere plek om deze definities op te slaan.

2.1. JPQL

standaard gebruikt de query definitie JPQL.

laten we eens kijken naar een eenvoudige repository methode die actieve gebruikersentiteiten uit de database retourneert:

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

2.2. Native

we kunnen ook native SQL gebruiken om onze query te definiëren. Het enige wat we hoeven te doen is de waarde van het nativequery attribuut op true instellen en de native SQL query definiëren in het value attribuut van de annotatie:

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

definieer volgorde in een Query

kunnen we een extra parameter van type Sort doorgeven aan een Spring Data method declaratie die de @Query annotatie heeft. Het zal worden vertaald in de volgorde van clausule die wordt doorgegeven aan de database.

3.1. Sorteren voor JPA verstrekte en afgeleide methoden

voor de methoden die we krijgen uit de doos, zoals findAll(Sort) of degenen die worden gegenereerd door parsing methode handtekeningen, kunnen we alleen object Eigenschappen gebruiken om onze Sorteer:

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

stel je nu voor dat we willen Sorteren op de lengte van een naameigenschap:

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

wanneer we de bovenstaande code uitvoeren, krijgen we een uitzondering:

org.Spring Framework.gegevens.mapping.PropertyReferenceException: geen eigenschap lengte (Naam) gevonden voor type gebruiker!

3.2. JPQL

als we JPQL gebruiken voor een query definitie, dan kunnen Spring Data Sorteren zonder enig probleem-alles wat we hoeven te doen is een methodeparameter van type Sorteren toevoegen:

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

we kunnen deze methode aanroepen en een Sorteerparameter doorgeven, die het resultaat ordent met de naameigenschap van het gebruikersobject:

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

en omdat we de @ Query-annotatie hebben gebruikt, kunnen we dezelfde methode gebruiken om de gesorteerde lijst met gebruikers te krijgen op basis van de lengte van hun namen:

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

het is cruciaal dat we JpaSort gebruiken.unsafe () om een Sorteer object instantie te maken.

wanneer we:

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

dan krijgen we precies dezelfde uitzondering als we hierboven zagen voor de findAll() methode.

wanneer Spring Data de onveilige sorteervolgorde ontdekt voor een methode die de @ Query-annotatie gebruikt, dan voegt het alleen de sorteerclausule toe aan de query — het slaat de controle over of de eigenschap op te sorteren tot het domeinmodel behoort.

3.3. Native

wanneer de @ Query-annotatie native SQL gebruikt, is het niet mogelijk om een soort te definiëren.

als we dat doen, krijgen we een uitzondering:

org.Spring Framework.gegevens.jpa.repository.query.InvalidJpaQueryMethodException: kan geen native query ’s gebruiken met dynamische sortering en/of paginering

zoals de uitzondering zegt, wordt de sortering niet ondersteund voor native query’ s. De foutmelding geeft ons een hint dat paginering ook een uitzondering zal veroorzaken.

er is echter een tijdelijke oplossing die paginering inschakelt, en we zullen het in de volgende sectie behandelen.

paginering

paginering stelt ons in staat om slechts een deelverzameling van een geheel resultaat in een pagina terug te geven. Dit is bijvoorbeeld handig bij het navigeren door meerdere pagina ‘ s met gegevens op een webpagina.

een ander voordeel van paginering is dat de hoeveelheid gegevens die van server naar client wordt verzonden, geminimaliseerd wordt. Door het verzenden van kleinere stukjes data kunnen we over het algemeen een verbetering van de prestaties zien.

4.1. JPQL

het gebruik van paginering in de jpql query definitie is eenvoudig:

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

we kunnen een pagerequest parameter doorgeven om een pagina met gegevens te krijgen.

paginering wordt ook ondersteund voor native queries, maar vereist een beetje extra werk.

4.2. Native

we kunnen paginering inschakelen voor native query ‘ s door een extra attribuut countQuery te declareren.

dit definieert de SQL die moet worden uitgevoerd om het aantal rijen in het hele resultaat te tellen:

@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 versies vóór 2.0.4

de bovenstaande oplossing voor native queries werkt prima voor Spring Data JPA versies 2.0.4 en later.

voorafgaand aan die Versie, wanneer we een dergelijke query proberen uit te voeren, krijgen we dezelfde uitzondering die we beschreven hebben in de vorige sectie over Sorteren.

we kunnen dit oplossen door een extra parameter toe te voegen voor paginering in onze query:

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

in het bovenstaande voorbeeld voegen we “\n- # pageable\n ” toe als de plaatshouder voor de parameter paginering. Dit vertelt Spring Data JPA hoe de query te ontleden en injecteren van de pageable parameter. Deze oplossing werkt voor de H2 database.

we hebben besproken hoe eenvoudige SELECT query ‘ s te maken via JPQL en native SQL. Vervolgens laten we zien hoe je extra parameters definieert.

geïndexeerde Query Parameters

er zijn twee manieren waarop we methodeparameters aan onze query kunnen doorgeven: geïndexeerde en benoemde parameters.

in deze sectie behandelen we geïndexeerde parameters.

5.1. JPQL

voor geïndexeerde parameters in JPQL, zullen de Voorjaarsgegevens methodeparameters doorgeven aan de query in dezelfde volgorde als ze in de methodedeclaratie voorkomen:

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

voor de bovenstaande query ‘ s zal de status methode parameter worden toegewezen aan de query parameter met index 1, en de naam methode parameter zal worden toegewezen aan de query parameter met index 2.

5.2. Native

geïndexeerde parameters voor de native queries werken precies op dezelfde manier als voor JPQL:

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

In de volgende sectie laten we een andere aanpak zien: parameters doorgeven via naam.

benoemde Parameters

we kunnen ook methodeparameters doorgeven aan de query met benoemde parameters. We definiëren deze met behulp van de @ Param annotatie in onze repository method declaration.

elke parameter geannoteerd met @Param moet een waardestring hebben die overeenkomt met de corresponderende jpql-of SQL-queryparameternaam. Een query met benoemde parameters is gemakkelijker te lezen en is minder foutgevoelig voor het geval de query moet worden herwerkt.

6.1. JPQL

zoals hierboven vermeld, gebruiken we de @ Param annotatie in de method declaration om parameters gedefinieerd door naam in JPQL te matchen met parameters uit de method declaration:

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

merk op dat we in het bovenstaande voorbeeld onze SQL-query en methodeparameters hebben gedefinieerd om dezelfde namen te hebben, maar het is niet vereist zolang de waardestrings hetzelfde zijn:

@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

voor de native query definitie is er geen verschil in hoe we een parameter via de naam doorgeven aan de query in vergelijking met JPQL — we gebruiken de @ Param annotatie:

@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. Collection Parameter

laten we het geval bekijken wanneer de where clausule van onze jpql of SQL query het in (of niet IN) sleutelwoord bevat:

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

In dit geval kunnen we een zoekmethode definiëren die verzameling als parameter neemt:

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

aangezien de parameter een verzameling is, kan deze worden gebruikt met List, HashSet, enz.

vervolgens laten we zien hoe u gegevens kunt wijzigen met de @Modifying annotation.

queries bijwerken met @ Modifying

we kunnen de @ Query-annotatie gebruiken om de status van de database te wijzigen door ook de @Modifying annotation toe te voegen aan de repository-methode.

8.1. JPQL

de repository methode die de gegevens wijzigt heeft twee verschillen in vergelijking met de SELECT query — het heeft de @ Modifying annotation en, natuurlijk, de jpql query gebruikt update in plaats van select:

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

de return waarde bepaalt hoeveel rijen de uitvoering van de query bijgewerkt. Zowel geïndexeerde als benoemde parameters kunnen worden gebruikt in updatequeries.

8.2. Native

we kunnen de status van de database ook wijzigen met een native query. We hoeven alleen maar de @Modifying annotation toe te voegen:

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

8.3. Inserts

om een insert-bewerking uit te voeren, moeten we zowel @modify toepassen als een native query gebruiken, omdat INSERT geen deel uitmaakt van de JPA-interface:

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

dynamische Query

vaak zullen we geconfronteerd worden met de noodzaak om SQL-statements te bouwen op basis van voorwaarden of datasets waarvan de waarden alleen bekend zijn tijdens runtime. In die gevallen kunnen we niet zomaar een statische query gebruiken.

9.1. Voorbeeld van een dynamische Query

bijvoorbeeld, laten we ons een situatie voorstellen waarin we alle gebruikers moeten selecteren wiens e-mail is als een van een set gedefinieerd tijdens runtime-email1, email2,…, emailn:

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

omdat de set dynamisch is geconstrueerd, kunnen we tijdens het compileren niet weten hoeveel soortgelijke clausules we moeten toevoegen.

in dit geval kunnen we niet gewoon de @Query-annotatie gebruiken omdat we geen statisch SQL-statement kunnen leveren.

in plaats daarvan, door een aangepaste composite repository te implementeren, kunnen we de basis jparepository functionaliteit uitbreiden en onze eigen logica bieden voor het bouwen van een dynamische query. Laten we eens kijken hoe we dit moeten doen.

9.2. Aangepaste Repositories en de JPA Criteria API

gelukkig voor ons biedt Spring een manier om de basis repository uit te breiden door het gebruik van aangepaste fragment interfaces. We kunnen ze dan aan elkaar koppelen om een composiet repository te maken.

we beginnen met het maken van een aangepaste fragment-interface:

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

en dan zullen we het implementeren:

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

zoals hierboven getoond, hebben we gebruik gemaakt van de JPA Criteria API om onze dynamische query op te bouwen.

ook moeten we ervoor zorgen dat de Impl postfix in de class naam wordt opgenomen. Spring zal zoeken in de UserRepositoryCustom implementatie als UserRepositoryCustomImpl. Omdat fragmenten niet alleen repositories zijn, vertrouwt Spring op dit mechanisme om de fragment-implementatie te vinden.

9.3. Het uitbreiden van de bestaande Repository

merk op dat alle zoekmethoden van Sectie 2 tot en met Sectie 7 in het gebruikersrepository zijn.

dus, nu zullen we ons fragment integreren door de nieuwe interface uit te breiden in het gebruikersrepository:

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

9.4. Met behulp van de Repository

en tot slot kunnen we onze dynamische query-methode aanroepen:

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

we hebben met succes een composite repository gemaakt en onze aangepaste methode genoemd.

conclusie

In dit artikel hebben we verschillende manieren behandeld om query ‘ s te definiëren in Spring Data JPA-repositorymethoden met behulp van de @Query-annotatie.

We hebben ook geleerd hoe we een aangepaste repository kunnen implementeren en een dynamische query kunnen maken.

Zoals altijd zijn de volledige code voorbeelden gebruikt in dit artikel beschikbaar op GitHub.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.