Le nom des méthodes
Dans une interface Repository de JPA, un nom de méthode peut servir à générer une requête SQL. Par exemple, vous me croierez ou pas, mais quand j’ai commencé à tapperl un nom de méthode dans mon interface Repository, voici que mon IDE m’a proposé :
List<TruckModel
> findByBrand(String brand);
La requête qui va être générée par ceci est la suivante :
Select * from truck_model where brand = :brand;
Où :brand est la variable brand en paramètre. Vous n’avez pas écrit une ligne SQL et comme par magie (la seule magie qui existe étant celle du père noël, vous comprendrez qu’ici ça n’en est pas) vous avez une requête qui est généré et dont le résultat est mis dans un objet JAVA. Là c’est une requête simple, mais voyons ce que l’on peut faire de plus. Par exemple :
List<TruckModel> findByBrandAndNameContains(String brand, String name);
Cela va produire ceci :
Select * from truck_model where brand = :brand and name like ‘%:name%’;
Où :name est la variable name passée en paramètre. Vous me direz : « mais par quelle diablerie cela est il possible? », et bien c’est un peu ce que l’on attend d’un ORM. Vous allez pouvoir trouver tout un tas de mots clés qui vont permettre de faire vos requêtes de plus en plus complexe.
Les requêtes @Query
En faisant des requêtes comme la précédente, vous pourriez obtenir des méthodes de 300km de long. Si la distance ne vous fait pas peur, vous pouvez essayer de faire toutes vos requếtes de la façon précédente. Nez en moins ( ce n’est pas une référence à Voldemort) ce n’est pas ce qui est conseillé. Quand la requête devient trop complexe, ou pour faire une recherche dans plusieurs colonnes, on passe aux query. Cela consiste à donner un nom à la méthode, qui a un sens pour votre équipe, et mettre une annotation @Query au dessus avec la requête. Une requête comprends le nom de la classe et les attributs de la classe, pas le nom des tables et des colonnes. Sauf si comme moi, vous avez surchargé le nom de la table avec @Entity(name= »truck »). Cela donne par exemple :
@Query("SELECT t FROM truck t where name like concat(:begin, '%')")
List<TruckModel> maisOuQuiSontLesCamionsQuiCommencePar(@Param("begin") String begin);
Donc on a bien donné le nom que l’on voulait à la méthode, cela n’a pas d’incidence sur la requête. On utilise @Param afin de passer les paramètres à la requête. On peut alors se servir des paramètres dans la requête.
Ajoutons un peu de complexité pour aller plus loins, on va ajouter une classe WheelModel :
package com.raphlys.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity()
public class WheelModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String brand;
@Column(unique = true)
private String size;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
}
On modifie TruckModel afin qu’il soit associé à cette classe
package com.raphlys.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
@Entity(name = "truck")
public class TruckModel {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String brand;
@Column(unique = true)
private String name;
@OneToOne
private WheelModel wheel;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public WheelModel getWheel() {
return wheel;
}
public void setWheel(WheelModel wheel) {
this.wheel = wheel;
}
}
J’ai fait une relation OneToOne, afin de nous simplifier la vie pour cette exercice, mais je n’ai jamais vue de camion avec une seule roue. Je peux avec JPA faire cette requête :
@Query("SELECT t FROM truck t where t.wheel.brand = :brand")
List<TruckModel> findByWheelBrand(@Param("brand") String brand);
On peut directement faire un filtre sur un attribut d’un attribut de notre classe N’est ce pas magnifique? Pas besoin de faire une jointure. C’est JPA qui s’en occupe, « tu s’occupes de rien, JPA s’occupe de tout ». Au cas où, ce n’est pas évident, on va récupérer tous les camions qui ont leur roue qui est de la marque passée en paramètre.
Maintenant, que l’on a fait ça. Il peut arriver, qu’on ait besoin de faire des jointures complexes, peut être avec des tables qui n’ont pas vocation à être dans JPA. Disons que vous vouliez faire une requête complexe, difficile à maintenir et vous avez un pro du SQL dans votre équipe qui peut l’optimiser à fond. Vous pouvez le faire comme ceci :
@Query(value = "SELECT * FROM truck t INNER JOIN wheel w ON t.wheel_id = w.id WHERE w.brand = :brand", nativeQuery = true)
List<TruckModel> findByWheelBrand(@Param("brand") String brand);
Ça fonctionne aussi, c’est moins jolie, moins pratique… Mais dans certains cas ça peut être utile. En général les cas complexes, on a plus l’habitude de raisonné avec SQL qu’avec JPA
Laisser un commentaire