Spring Data JPA
Spring permet d'utiliser un ORM implémentant JPA et vous permet de ne pas avoir à écrire de requêtes SQL.
Par défaut, l'ORM utilisé par Spring est Hibernate qui implémente la norme JPA.
Bien qu'il soit possible d'utiliser XML pour faire du mapping avec Hibernate, la méthode privilégiée est l'utilisation d'annotations Java.
Mappings
@Entity
pour indiquer à JPA que c'est une classe persistée en base
@Table
optionnel pour indiquer un nom et des propriétés
@Entity
@Table( name = "articles" )
public class Article {
...
}
@Id
permet d'indiquer la clé primaire
@GeneratedValue(strategy = GenerationType.IDENTITY)
pour indiquer que la clé est générée automatiquement
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // avec un type serial en postgres
private Long id;
Par défaut, toutes les propriétés de l'objet seront mappées sur des colonnes.
@Column
permet de préciser un nom.
@Transient
permet d'indiquer qu'une propriété ne doit pas être mappée en base.
@Entity
@Table(name="articles")
public class Article {
private String titre;
@Column(name = "contenu")
private String corps;
@Transient
private String nePasStockerEnBase;
}
Il est possible de mapper les enum avec une colonne de type nombre ou texte.
@Enumerated(EnumType.ORDINAL) // ou EnumType.STRING
@Column(name = "statut")
private StatutArticle statut;
EnumType permet de dire si on stocke l'ordre de la valeur (ORDINAL -> int) ou son nom (STRING -> string)
L'annotation @ManyToOne
permet de gérer les clés étrangères, dans l'entité qui
possède la colonne FK.
@Entity
public class Commentaire {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String contenur;
@ManyToOne
@JoinColumn(name = "article_id")
private Article artcle;
}
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String titre;
}
Mappings
A l'inverse, il est possible dans l'entité qui ne possède pas la colonne de FK
de faire un @OneToMany
pour récupérer la liste des objets qui y font
référence.
targetEntity
permet de préciser la classe qui y fait référence, optionel car
Java peut la détecter automatiquement à partir du type de la liste. L'attribut
mappedBy
permet de dire quel attribut porte le @ManyToOne
dans l'entité
cible. C'est optionnel également s'il est nommé à partir du nom de classe.
@Entity
public class Categorie {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(targetEntity=Article.class, mappedBy="categorie")
private List<Article> articles = new ArrayList<>();
}
Mappings
Enfin, le @ManyToMany
permet de gérer les relations de type N:N. Il faut
absolument une table intermédiaire pour stocker les relations.
@Entity
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// correspond à une table de jointure avec deux colonnes
// (id_article, id_label)
@ManyToMany
private List<Label> labels = new ArrayList<>();
}
@Entity
public class Label {}
TP9 - Création de mappings JPA
Reprenez vos classes Article et Categorie et ajoutez les annotations pour faire le mapping avec votre base de données.
- Indiquez que ces classes correspondent à des entités gérées en base avec @Entity
- Précisez quel attribut de chacune des classes correspond à la clé primaire avec @Id
- Indiquez le nom des colonnes pour quelques attributs avec @Column
- Gérez la foreign key entre Article et Category avec @ManyToOne et @OneToMany
Transactions
L'annotation @Transactional
sur une classe ou une méthode publique permet de
gérer automatiquement la transaction en base de données.
La transaction n'aura lieu que si la méthode est appelée depuis une autre classe.
// service transactionnel
@Service
@Transactional
public class MonService() {
public void methode1() {};
public void methode2() {};
}
// méthode transactionnelle
@Service
public class MonService() {
@Transactional
public void methode1() {};
}
Repository
L'interface JpaRepository<ENTITY, ID>
proposée par Spring vous permet de créer
un accès simple à vos données en base. En héritant de cette interface vous
pourrez utiliser des méthodes de récupération et d'enregistrement des entités en
base.
Méthodes héritées : findAll()
, findById()
, save()
, etc.
Vous pouvez ajouter vos propres méthodes, la requête sera créée automatiquement en analysant le nom de la méthode.
@Repository
public interface ArticleRepository
extends JpaRepository<Article, Long> {
// génère select * from article where titre = :titre
List<Article> findByTitre(String titre);
}
Possibilité de faire du JPQL / SQL et de garder la main sur les requêtes.
@Repository
public interface ArticleRepository
extends JpaRepository<Article, Long> {
@Query("select a from Article a where a.titre LIKE ?1")
Article findByTitre(String titre);
}
TP10 - JpaRepository
Créez deux classes @Repository qui implémentent l'interface JpaRepository
- Pour l'entité Article
- Pour l'entité Categorie
Créez une méthode de controller qui permet d'envoyer un article en POST via Insomnia, et qui ensuite l'enregistre dans la base de données via son JpaRepository.