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 ).