Blue Flower

Chercher

I. Généralités sur l'API Criteria

1. Présentation de Criteria

Criteria est un API qui permet de faire des  requêtes sur des objets Entity rangés dans une base de données relationnelles comme on le fait avec JPQL; toutefois les  requêtes Criteria   sont de "type-safe", portables et faciles à modifier. Comme JPQL, l'API Criteria suit un schéma abstrait sur les instances des Entity concernées. L'API Metadata est associé à l'API Criteria pour permettre de modéliser la persistance des JPA Entity.

Comme L'API Criteria fonctionne sur le schéma abstrait des instances des Entity concernées, il  permet  de trouver, modifier ou  supprimer des Entity  persistantes en invoquant seulement des opérations Java sur des JPA Entity. C'est L'API Metamodel en fonctionnant de concert avec l'API Criteria qui permet de modéliser les objets des  classes des JPA Entity  pour les requêtes Criteria.

Avec l'API Criteria, contrairement à JPQL, les erreurs sont détectées plus tôt pendant la phase de compilation.Toutefois les requêtes JPQL et les requêtes de JPA Creteria sont identiques en termes de performance et d'efficacité. Pour les requêtes statiques simples c'est à dire  les requêtes  basées sur des chaînes de caractères, par exemple les requêtes  nommées, mieux vaut utiliser  JPQL;  par contre, pour les requêtes dynamiques construites au moment de l'exécution, il faut privilégier plutôt l' emploi de l'API Criteria.

2. Structure de requête  Criteria

L'API Criteria et l'API JPQL sont étroitement liés et permettent d'utiliser les mêmes type de requêtes. L'API criteria se trouve dans le package "javax.persistence.criteria".
Une requête simple de Criteria renvoie toutes les instances d'une classe JPA Entity présente dans la base de données.

2.1. L'interface CriteriaBuilder

 Il existe deux interfaces  principales utilisées pour créer des requêtes avec L' API Criteria: c'est l'interface CriteriaBuilder et l'interface CriteriaQuery. Dans un premier temps on doit obtenir un cadre pour un objet CriteriaBuilder pour pouvoir  ensuite  créer un objet CriteriaQuery;  pour cela on utilise un objet EntityManager. On voit dans l'exemple ci-dessous les différentes étapes à suivre pour faire la requête proprement dite.

On définit CriteriaBuilder  pour pouvoir créer des objets de  type CriteriaQuery; les méthodes ci-dessous permettent de créer des objets de type  CriteriaQuery:

  • CreateQuery () – Cette méthode crée  un objet de type CriteriaQuery - CriteriaQuery<Object>  createQuery() - .
  • CreateQuery (Classe) – Crée un CriteriaQuery utilisant des génériques pour éviter le casting du résultat de la classe.
  • createTupleQuery () – Crée un CriteriaQuery qui retourne «un map» comme des Tuples d’objets , à la place de l’objet tableaux pour les requêtes multiselect.
  • createCriteriaDelete (Classe) – Crée un CriteriaDelete qui permet de supprimer un lot d’objets directement sur la base de données ( voir JPA 2.1).
  • createCriteriaUpdate (Classe) – Crée un CriteriaUpdate qui permet de mettre à jour un lot d’objets directement sur la base de données (JPA 2.1).

CriteriaBuilder définit également toutes les opérations de comparaison pris en charge et des fonctions utilisées pour définir les clauses de la requête.
Cette classe possède d'autres méthodes qui permettent de prendre en compte d'autres problématiques des requêtes sur des instances des JPA Entity de l'application à développer. ....

2.2. Exemples

La simple requête Criteria ci-dessous retourne toutes les instances de l'Entity P présentes dans la base de données:

EntityManagerFactory factory = Persistence.createEntityManagerFactory("Eclipselink_JPA");
EntityManager em = factory.createEntityManager(); CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery cq = cb.createQuery(Pet.class); Root pet = cq.from(Pet.class); cq.select(pet); TypedQuery q = em.createQuery(cq); List allPets = q.getResultList();

La requête JPQL équivalente est:

SELECT p
FROM Pet p

Ci-dessous on a les différentes étapes suivies pour créer une requête Criteria:

  • Utiliser une instance EntityManager pour créer un objet CriteriaBuilder.
  • Crée un objet Query en créant une instance de l'interface Criteriaquery. Les attributs de l'objet Query  seront modifiables par les détails de la requête.
  • Définir un objet de type Root en faisant appel à la méthode "from" de de l'objet CriteriaQuery.
  • Spécifiez le type de résultat de la requête en appelant la méthode select de l'objet CriteriaQuery.
  • Préparer la requête pour l'exécution en créant une instance TypedQuery, en spécifiant le type de résultat de la requête.
  • Exécutez la requête en appelant la méthode getResultList de l'objet TypedQuery. Comme cette requête retourne une collection d'entités, le résultat est stocké dans une liste.

En reprenant en détail le bout de programme qui illustre l'utilisation de l'API Criteria, on voit bien les six étapes qu'on a définies:
1) Pour créer une instance de CriteriaBuilder, on appelle la méthode getCriteriaBuilder d'une instance de EntityManager:

CriteriaBuilder cb = em.getCriteriaBuilder(); 

2) On utilise l'instance CriteriaBuilder pour créer un objet de type Query:

 CriteriaQuery cq = cb.createQuery(Pet.class);

3) L'obet Query retournera  des instances  de l'Entity Pet. Pour créer un objet Query  "typesafe", on spécifie le type de la requête lorsqu'on crée l'objet CriteriaQuery. On  appelle ensuite la méthode from de l'objet Query pour définir la clause FROM de la requête; celle-ci retourne un objet Root:

Root pet = cq.from(Pet.class);

4) On appelle la méthode "select" de l'objet Query en lui passant comme paramètre l'objet de type Root; cela définit la clause de SELECT de la requête: 

 cq.select(pet);

5) Maintenant, on utilise l'objet Query pour créer un objet TypedQuery qui peut être exécuté sur la base de données. Les modifications apportées à l'objet Query sont saisies pour créer une requête prêt à exécuter:

TypedQuery q = em.createQuery(cq);

6) Exécutez cet objet Query en appelant sa méthode getResultList, car cette requête renverra plusieurs instances dde L'Entity. L'instruction suivante stocke les résultats dans un objet "List-collection-valued":

List allPets = q.getResultList();

...

précédent

suivant

1.Langage JPQL

1.1.  Généralités

JPQL est un langage de requêtes  proche de SQL et qui est défini par la spécification JPA; il permet de gérer la persistance de données dans des applications java. Avec ce langage développé avec  la syntaxe SQL on fait des requêtes sur des JPA Entity mappées dans des tables de bases de données relationnelles .
JPQL  récupère des informations ou des données en utilisant la clause SELECT, effectue des mises à jour à l'aide de la clause UPDATE et la clause DELETE.

1.2. Interfaces Query et TypedQuery

Dans la spécification JPA 2 les requêtes  sont représentées par deux interfaces; l'interface Query (déjà introduite dès JPA 1) et l'interface TypedQuery introduite à partir de JPA 2. L'interface TypedQuery  dérive de Query.
Depuis JPA 2, l'interface Query est utilisée lorsque le type du  résultat de la requête est inconnu ou lorsqu'une requête renvoie des résultats polymorphes et que le plus petit dénominateur commun connu de tous les objets du résultat est Object.
Lorsqu'un type de résultat plus spécifique est attendu, les requêtes  utilisent l'interface TypedQuery. Il est plus facile d'exécuter des requêtes et de traiter les résultats  d'une manière sûre en utilisant l'interface TypedQuery.
L'exécution d'une requête JPQL passe par la création d'un objet de type Query à partir d'une instance d'EntityManager.
Il faut créer un objet de la classe EntityManager qu'on peut définir comme suite;
EntityManager em = ... ;
Une fois qu'on ait créé une instance de EntityManager , l'exécution d'une requête JPQL se fait en deux temps :
On définit l'objet requête de type Query avec la methode createQuery() de "em" qui retourne l'unique objet résultat de la requête; Si la requête retourne plusieurs objets, une exception de type NonUniqueResultException est levée.
On exécute la requête, et on récupère le résultat.
Notons que la classe Query possède également la méthode getSingleResult()  qui peut servir .

1.3. Structure d'une requête JPQL

Comme on l'a dit ci-dessus le syntaxe des requêtes JPQL ressemble beaucoup à celui de SQL. Toutefois SQL permet de faire des requêtes directement sur les champs des tables  de la base de données et JPQL lui requête sur des objets Entity.
D'une façon générale la structure d'une requête JPQL est de la forme:

SELECT ... FROM ...
[WHERE ...]
[GROUP BY ... [HAVING ...]]
[ORDER BY ...]

La requête SELECT et la clause FROM sont requises à chaque fois qu'on veut récupérer des données, par contre les requêtes de mise à jour et de suppression de données ont une forme légèrement différente. Les autres clauses JPQL à savoir  WHERE, GROUP BY, HAVING et ORDER BY sont facultatives.

Les structures des requêtes  DELETE et  UPDATE de JPQL sont:

DELETE FROM ... [WHERE ...]
 
UPDATE ... SET ... [WHERE ...]

1.4. Faire une requête avec la méthode createQuery ou createNamedQuery

Les méthodes "EntityManager.createQuery()" et "EntityManager.createNamedQuery()" servent à faire des requêtes sur une base de données en utilisant le langage de requêtes JPQL. La méthode createQuery est utilisée pour créer des requêtes dynamiques, c'est à dire des requêtes définies directement dans la logique métier d'une application comme on le voit ci-dessous:

public List findWithName(String name) {
return em.createQuery(
    "SELECT c FROM Customer c WHERE c.name LIKE :custName")
    .setParameter("custName", name)
    .setMaxResults(10)
    .getResultList();
}

On voit bien ici que la requête va être construite au moment de l’exécution de l'application; d'où l'utilisation de la méthode createQuery(...).

La méthode createNamedQuery est utilisée pour créer des requêtes statiques ou des requêtes définies dans les métadonnées à l'aide de l'annotation javax.persistence.NamedQuery. Cette annotation @NamedQuery spécifie le nom de la requête qui sera utilisée avec la méthode createNamedQuery; ce qui est illustré dans le code ci-dessous:

@NamedQuery(
    name="findAllCustomersWithName",
    query="SELECT c FROM Customer c WHERE c.name LIKE :custName"
)
...
@PersistenceContext
public EntityManager em;
...
customers = em.createNamedQuery("findAllCustomersWithName")
    .setParameter("custName", "Smith")
    .getResultList();

Comme pour la plupart des autres opérations dans JPA, on voit bien que si on fabrique des requêtes , on utilise un EntityManager (représenté par em dans les extraits de code suivants); c'est en quelque sorte une fabrique pour Query et TypedQuery;

  
   Query q1 = em.createQuery("SELECT c FROM Country c");
  TypedQuery q2 = em.createQuery("SELECT c FROM Country c", Country.class);

Dans le code ci-dessus, la même requête JPQL qui récupère tous les objets Pays dans une table de la base de données est représentée à la fois par q1 et q2. Lors de la construction d'une instance TypedQuery, le type de résultat attendu doit être passé comme argument supplémentaire, comme démontré pour q2. Comme dans ce cas le type du résultat est connu (la requête renvoie uniquement les objets Pays), on utilise TypedQuery.
Il existe un autre avantage qui incite à utiliser TypedQuery. Dans un contexte de requêtes identique à celui ci-dessus, s'il n'existe pas encore d'instances de pays dans la base de données et que la classe Pays est inconnue, seule la variante TypedQuery est valide car elle introduit la classe Country dans la base de données.

.......

1.5. Exemple de fonctions simples et de fonctions imbriquées

Les fonctions simples retournent des valeurs basées sur des données d'entrée. Les fonctions imbriquées retournent les valeurs résultantes de calcul issus des données d'entrée. Ici on va créer une application qui illustre ces deux types de fonctions; On suppose avoir une table mappée issues d'un Entity appelé Employee et qui a un certain nombre d'attributs tels que: Eid, Ename, Edeg et Esalary:

package com.omara.eclipselink.service;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
public class ScalarandAggregateFunctions {
   public static void main( String[ ] args ) {
      EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "Eclipselink_JPA" );
      EntityManager entitymanager = emfactory.createEntityManager();
      //fonction simple
     Query query = entitymanager.createQuery("Select UPPER(e.ename) from Employee e");
   List<String> list = query.getResultList();
   for(String e:list) {
         System.out.println("Employee NAME :"+e);
    }
   //fonction imbriquées
     Query query1 = entitymanager.createQuery("Select MAX(e.salary) from Employee e");
     Double result = (Double) query1.getSingleResult();
     System.out.println("Max Employee Salary :" + result);
   }
}

...... 

2. Chargement de type Eager ou de type Lazy

Un des concepts principaux dans la spécification de JPA c'est le faite de faire une copie de la base de données en mémoire cache. Lorsque dans une application il y a  des transactions sur des données présentes dans la base de données, celles-ci affectent d'abord les données dupliquées qui sont en mémoire cache, et c'est uniquement lorsque les données sont validées par le gestionnaire de JPA Entity que les modifications sont effectuées dans la base de données.

Si avec JPA on doit charger une Entity (entité) depuis la base de données (qu'il s'agisse par un appel à EntityManager.find(...) ou par une requête), on doit être précis sur les informations à charger. Est ce qu'on doit-il charger tous les attributs de l'Entity?  et parmi ces attributs, doit-on  charger les Entity  qui sont en relation avec l'Entity chargée ? Ces questions sont importantes, car la façon d'y répondre peut avoir un impact sur les performances de l'application.
Dans JPA, l'opération de chargement d'une entité depuis la base de données est appelée fetch; Il existe deux façons de charger des enregistrements depuis la base de données: soit le chargement se fait par stratégie Eager (chargement  impatiente) ou  par stratégie Lazy (chargement différé ou paresseux).

3. Chargement immédiat - Eager fetch

Dans cette stratégie de chargement on charge l'enregistrement entier de la base en utilisant la clé primaire. Dans ce cas les données associées à l'Entity sont récupérées immédiatement; Cette stratégie est appliquée par défaut pour les annotation  @Basic, @OneToOne et @ManyToOne.

4. Chargement différé - Lazy fetch

Dans cette stratégie de chargement, Il y a une vérification de la possibilité de l'extraction de L'Entity avec la clé primaire. Ensuite, plus tard, si on utilise des méthodes getterXXX  de cette Entity on extrait l'ensemble de l'Entity. C'est la stratégie Lazy qui est utilisée lorsqu'on charge l'enregistrement pour la première fois. De cette façon, une copie de l'enregistrement entier est stockée dans la mémoire cache. Cette stratégie est appliquée par défaut pour les annotations  @OneToMany et @ManyToMany

5. Stratégies d'héritage

JPA est un API défini au sein du  langage Java. Par conséquent, il prend en charge tous les concepts orientés objet pour la persistance des Entity. L'héritage est le concept central du langage orienté objet, donc nous pouvons utiliser des relations d'héritage ou des stratégies  d'héritage entre les Entity. On va voir que JPA prend en charge trois types de stratégies d'héritage telles que SINGLE_TABLE, JOINED_TABLE et TABLE_PER_CONCRETE_CLASS.

Single Table strategy

La stratégie Single-Table (de table unique) prend tous les champs de classes (super et sous-classes) et les mappe dans une seule table connue sous le nom de stratégie SINGLE_TABLE
.....

....

(à suivre)..

précédent

Création d'un projet JPA

1. Un projet JPA avec Java SE

1.1. Les pré-requis

 Dans cet article on va voir différents aspect de JPA en développant un projet simple en s'appuyant au technologies suivantes:

  • maven >= 3.0
  • JPA 2.1
  • Hibernate
  • H2

Dans cet exemple, on va utiliser utiliserons Maven pour configurer nos dépendances requises. On vu que JPA n'est qu'une spécification et pour l'implémenter Il existe beaucoup d'implémentations gratuites disponibles comme EclipseLink, Hibernate, etc... . Dans notre exemple on va utiliser Hibernate pour l'implémentation de JPA et comme  base de données on utilisera MySQL.
Pour commencer, Créons d'abord un projet maven simple. On va    créer  un projet  en utilisant un archétype de démarrage (voir un autre tuto pour cette création).

 

.....

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.omara.javaExemples</groupId>
  <artifactId>jpa-exemple</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>jpa-exemple</name>
  <url>http://ocamil.com</url>
  <properties>
    <java.version>1.8</java.version>
    <hibernate.version>4.3.6.Final</hibernate.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <!-- JPA -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <!-- For connection pooling -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-c3p0</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <!-- Database -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
     <version>5.1.31</version>
    </dependency>
    <!-- Test -->
    <dependency>
     <groupId>junit</groupId>
      <artifactId>junit</artifactId>
     <version>4.11</version>
      <scope>test</scope>
      <exclusions>
       <exclusion>
        <groupId>org.hamcrest</groupId>
         <artifactId>hamcrest-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
    <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-all</artifactId>
      <version>1.3</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
       <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source>${java.version}</source>
          <target>${java.version}</target>
          <compilerArgument>-Xlint:all</compilerArgument>
          <showWarnings>true</showWarnings>
          <showDeprecation>true</showDeprecation>
       </configuration>
      </plugin>
    </plugins>
  </build>
</project>
....
 

(à suivre)..

précédent