Java Persistence API (la suite)
L'héritage dans JPA
Héritage avec table unique
Nous allons étudier la représentation d'un arbre d'héritage dans une structure relationnelle. Définissons trois classes : une pour les UE, une autre (qui hérite de la première) pour les UE de master et une troisième (qui hérite également de la première) pour les UE de Licence :
package myapp.jpa.model; import jakarta.persistence.Basic; import jakarta.persistence.Entity; import jakarta.persistence.Id; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Data @NoArgsConstructor @AllArgsConstructor public class UE { @Id private String code; @Basic private int ects; }
Les UE de Master :
package myapp.jpa.model; import jakarta.persistence.Basic; import jakarta.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Entity @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) public class MasterUE extends UE { @Basic private String masterName; public MasterUE(String code, int ects, String masterName) { super(code, ects); this.masterName = masterName; } }
Les UE de Licence :
package myapp.jpa.model; import jakarta.persistence.Basic; import jakarta.persistence.Entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Entity @Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = true) public class LicenceUE extends UE { @Basic private String description; public LicenceUE(String code, int ects, String description) { super(code, ects); this.description = description; } }
Héritage avec table de jointure
Dans cette deuxième stratégie, les classes sont représentées par plusieurs tables mais les propriétés communes sont représentées une seule fois :
... @Entity @Inheritance(strategy = InheritanceType.JOINED) @Data @NoArgsConstructor @AllArgsConstructor public class UE { ... }
Héritage avec tables séparées
Dans cette troisième stratégie, les classes sont représentées par plusieurs tables spéparées :
... @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) @Data @NoArgsConstructor @AllArgsConstructor public class UE { ... }
Requêtes construites par programmation
Jusqu'à maintenant nous avons directement utilisé des requêtes JPQL sous la forme de chaîne de caractères éventuellement paramétrées. A partir de JPA 2 il est possible de construire dynamiquement une requête bien typée à partir d'une API.
public <T> Collection<T> findAll(Class<T> clazz) { ... }
Utiliser Spring Data
Maintenant que vous connaissez un peu mieux le fonctionnement de JPA, nous allons utiliser Spring-data pour supprimer le code des classes Dao.
1) Commencez par créer l'interface ci-dessous :
package myapp.jpa.dao; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import myapp.jpa.model.Person; public interface PersonRepository extends JpaRepository<Person, Long> { List<Person> findByFirstName(String name); List<Person> findByFirstNameLike(String name); }
2) Ajoutez ensuite une clause à votre classe de configuration pour activer le traitement des JpaRepository :
package myapp.jpa.dao; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import myapp.jpa.model.Person; @Configuration @EntityScan(basePackageClasses = Person.class) // NOUVEAU @EnableJpaRepositories(basePackageClasses = SpringDaoConfig.class) public class SpringDaoConfig { }
3) Terminez par une classe de test :
package myapp.jpa.dao; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Date; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import myapp.jpa.dao.PersonRepository; import myapp.jpa.model.Person; @SpringBootTest public class TestPersonRepository { @Autowired PersonRepository dao; @Test public void testRepository() { // détruire les instances dao.deleteAll(); assertFalse(dao.findAll().iterator().hasNext()); // créer une instance var p = new Person("AAA", new Date()); dao.save(p); // tester une instance var op = dao.findById(p.getId()); assertTrue(op.isPresent()); p = op.get(); assertEquals("AAA", p.getFirstName()); } }
- Enrichissez votre classe de test pour tester les méthodes findByFirstName et findByFirstNameLike.
- Avec cette documentation, ajoutez une méthode deleteLikeFirstName(String pattern) à la classe PersonRepository (utilisez @Query et @Transactional ).