Accès avancé aux données
relationnelles
Frédéric Claux – 2019
Faculté des Sciences et Techniques
CC BY-NY-CD - [email protected] 2019
Plan
• Définir les données
• Problématiques métier
• Stocker les données
• Persistence
• Accéder aux données
• Création, suppression, mise à jour
• Requêtage
• Confort ou inconfort lié à ces opérations
Définir les données
• Définir des concepts et problématiques métier
• Définir les données telles qu’elles sont
Accéder aux données
• Requêtage
• Puissance/flexibilité du langage de requête ?
• Rapidité ?
Confort d’accès aux données
• Faciliter l’interaction avec le code
• Car le code est omniprésent
• Le code est le point de départ pour le développeur
• Limiter l’infrastructure
• Couches…
• Limiter les possibilités de faire des erreurs
• Stocker les données doit-il être un travail à part entière pour le
développeur ?
• Si les données sont trop volumineuses (eg. pour tenir en mémoire)
• Accéder aux données sous leur forme stockées ?
Représentation des données
• Objets (« POJO »)
• Interaction triviale avec le code et ses classes proprement définies
• XML
• Parsing facile
• Requêtage puissant possible (XSLT, XQuery)
• Bases SQL
• Requêtage extrêmement puissant
• Scalabilité
• Typage fort (schema)
• Autres bases
• Performance
• Scalabilité
• etc.
POJO (définition directe sous forme d’objets)
• Pour
• Définir de données sous forme de classes
• Requêtage avec la Stream API
• Persistence simple à réaliser
• Contre
• Requêtage opère sur des données qui doivent être (toutes) stockées en
mémoire
XML
• Populaire au début des années 2000
• Tout-XML (vers une interopérabilité généralisée des données ?)
• Pour
• Parsing simplifié. Edition manuelle relativement simple pour un utilisateur
• Typage fort via la possibilité de définir des schémas (XSD)
• Requêtage (et transformation de données) via XQuery et XSLT
• Contre
• Le tout-XML ne s’est pas produit
• Cela aurait pu, mais cela n’a pas été le cas
• Vivre entièrement dans l’univers XML possible pour des traitements, mais lourd
• XSLT vs code (lourdeur du XSLT incite à la prudence)
• Dès lors, quelle réelle carte à jouer pour XML ?
Bases de données relationnelles
• Pour
• Format relationnel simple et puissant
• Requêtage particulièrement puissant (SQL)
• Requêtage opère sur des données présentes ou non en mémoire
• Instances distribuées possibles
• Transactions
• Interopérabilité avec le code possible (Oracle, SQL Server, H2…)
• Contre
• Plus lourd qu’XML ou POJO à mettre en place
• Interface de base pour le langage très limitée (chaîne textuelle SQL et rien d’autre)
• Typer les données pour le code non trivial (comme pour XML)
• SQL pas standard d’une base à l’autre
• SQL un peu austère au premier abord
Représentation des données
• Objets (« POJO »)
• Interaction triviale avec le code et ses classes proprement définies
• XML
• Parsing facile
• Requêtage puissant possible (XSLT, XQuery)
• Bases SQL
• Requêtage extrêmement puissant
• Scalabilité
• Typage fort (schema)
• Autres bases
• Performance
• Scalabilité
• etc.
Modèle relationnel
Modèle relationnel vs POJO
• Modèle relationnel = POJO sans mécanisme de définition virtuel
• Le modèle relationnel ne définit
pas de classes dérivées d’Employee,
ci-contre
• Les cardinalités entre les entités
sont définies naturellement dans le code
Publicité
avec les tableaux []
Bases de données relationnelles
• Pour
• Format relationnel simple et puissant
• Requêtage particulièrement puissant (SQL)
• Requêtage opère sur des données présentes ou non en mémoire
• Instances distribuées possibles
• Transactions
• Interopérabilité avec le code possible (Oracle, SQL Server, H2…)
• Contre
• Plus lourd qu’XML ou POJO à mettre en place
• Interface de base pour le langage très limitée (chaîne textuelle SQL et rien d’autre)
• Typer les données pour le code non trivial (comme pour XML)
• SQL pas standard d’une base à l’autre
• SQL un peu austère au premier abord
Bases de données relationnelles
• Contre
• Plus lourd qu’XML ou POJO à mettre en place
• Une fois mis en place, le bénéfice est visible
• Interface de base pour le langage très limitée (eg. SQL seule API proposée)
• A améliorer. Proposer une API fortement structurée et typée.
• Typer les données pour le code non trivial (comme pour XML)
• A faire. Proposer un typage des données elles-mêmes.
• SQL pas standard d’une base à l’autre
• A faire (voir le second point sur l’API à proposer)
• SQL un peu austère au premier abord
• Pas réellement un argument
La base relationnelle en pratique
• Pluralité des applicatifs, des outils
• Caractère central (et souvent vital) de la base, caractérisant les
données métiers
Outil
Outil
Outil
Outil
Outil
Base
Outil
Rappels sur SQL (niveau L2)
String query =
"SELECT " +
"* " +
"FROM " +
"WHERE " +
"AND " +
"user_details " +
"email = ‘" + user.getEmail() + "’" +
"password = ‘" + user.getPassword() + "’";
Statement s = con.createStatement(query);
ResultSet rs = s.executeQuery();
Problèmes rencontrés
• Encodage et échappement des chaînes de caractères à faire soi-
même
• Risques d’injections malicieuses SQL
• Formattage des données à réaliser explicitement
• Dates en particulier (dépend de la locale utilisée par la base)
Requêtage SQL, bis
String query =
"SELECT " +
"* " +
"FROM " +
"user_details " +
"WHERE " +
"email = ? " +
"AND " +
"password = ?";
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, user.getEmail());
ps.setString(2, user.getPassword());
ResultSet rs = ps.executeQuery();
Améliorations apportées
• Encodage des chaînes de caractères automatiquement pris en charge
• Locale automatiquement prise en charge
• Dates prises en charge via type natif du langage (DateTime etc.)
Problèmes rencontrés
• Si la structure de la base est remise en question, le code
compile bien… mais ne marche plus
• Peut-on s’assurer en amont, dans l’IDE, que le code ne va pas compiler si la structure
de la base est altérée ?
• Pas de typage fort au sens général
• SELECT nom FROM client
• Mon environnement de développement peut-il me prévenir que ‘client’ existe et que
le champ ‘nom’ est bien disponible ?
• Pas de correspondance entre les tables SQL et des classes
• Pas indispensable (cf. exemple plus tard avec LINQ)
• Reste tout de même intuitif lorsque des requêtes renvoient des rangées de tables
entières/brutes)
Historique
• De nombreux langages de programmation existent
• C, C++, Java et tant d’autres
• De nombreux langages de requêtes (dédiés) existent
• SQL, XQuery…
• Intégration d’un langage de requêtage dans un langage de
programmation ?
Ordres SQL dans le langage
• Oracle Pro/C
• Disponible depuis plusieurs décennies
• Préprocesseur traduisant des ordres relationnels (SQL) directement écrits
dans le code (sans chaînes de caractères) en code C
• EXEC SQL <ordre SQL>
• Permet de partager des variables entre C et SQL
• SELECT INTO <variable C> par exemple
• Supporte les curseurs
Publicité
• Très utilisé ; toujours disponible
LINQ
• Language INtegrated Query
• C# et VB.NET
• Se branche sur différentes sources de données
• Objets (on parle parfois de Plain Old Objects)
• XML
• Base SQL…
• LINQ est directement intégré dans le langage
• Comme Oracle Pro/C
• Prise en charge par le compilateur lui-même
• Interop SQL/langage simplifiée
Possibilités offertes en Java
• Solutions propriétaires
• JOOQ
• Speedment
• Hibernate natif
• JINQ
• Solutions standardisées
• JPA (plusieurs implémentations disponibles)
• Stream API
• API avec typage fort des données
• API de requêtage fortement typée et/ou textuelle
• Certains services fournis par l’environnement de développement
Possibilités offertes en Java
• Solutions propriétaires
• JOOQ
• Speedment
• Hibernate natif
• JINQ
• Solutions standardisées
• JPA (plusieurs implémentations disponibles)
• Stream API
• API avec typage fort des données
• API de requêtage fortement typée et/ou textuelle
• Certains services fournis par l’environnement de développement
Ecosystème relationnel .NET
Base (SQL)
Objets
XML
Requête
VB.NET/C#
LINQ
Code applicatif
Objets statiquement ou
dynamiquement typés
• LINQ consomme des objets, mais aussi du SQL (direct), ou du XML
LINQ
• Le compilateur prend en charge la syntaxe
• Transforme le code en
• Requêtes SQL textuelles (lorsque le backend est une base SQL)
• Code C# d’interrogation sur des collections (lorsque le backend est une
collection d’objets et sous-objets)
• requêtes spécifiques lorsque le backend est XML
Ecosystème relationnel JPA
Base (SQL)
XML
Objets
JPQL
Code applicatif
Query
Objets statiquement ou
dynamiquement typés
• Deux API sont disponibles
• Une API explicite où les requêtes sont construites avec des objets précis
• Chaine de caractère JPQL. Dans ce cas JPQL transforme la chaine en requête fortement typée
à base d’instances d’objets
Exemple de requête
SELECT c FROM Country c WHERE c.population >
:p
Requête JPQL textuelle
CriteriaQuery<Country> q = cb.createQuery(Country.class);
Root<Country> c = q.from(Country.class);
ParameterExpression<Integer> p = cb.parameter(Integer.class);
q.select(c).where(cb.gt(c.get(Country_.population), p));
Requête JPQL objet
SELECT * FROM Country WHERE population >
<valeur>
Requête SQL textuelle
Exemple de requête
SELECT c FROM Country c WHERE c.population >
:p
Requête JPQL textuelle
CriteriaQuery<Country> q = cb.createQuery(Country.class);
Root<Country> c = q.from(Country.class);
ParameterExpression<Integer> p = cb.parameter(Integer.class);
q.select(c).where(cb.gt(c.get(Country_.population), p));
Requête JPQL objet
SELECT * FROM Country WHERE population >
<valeur>
Requête SQL textuelle
Typage de l’API de requêtage
SELECT c FROM Country c WHERE c.population >
:p
Requête JPQL textuelle
CriteriaQuery<Country> q = cb.createQuery(Country.class);
Root<Country> c = q.from(Country.class);
ParameterExpression<Integer> p = cb.parameter(Integer.class);
q.select(c).where(cb.gt(c.get(Country_.population), p));
Requête JPQL objet
SELECT * FROM Country WHERE population >
<valeur>
Requête SQL textuelle
Publicité
Typage des données manipulées
SELECT c FROM Country c WHERE c.population >
:p
Requête JPQL textuelle
CriteriaQuery<Country> q = cb.createQuery(Country.class);
Root<Country> c = q.from(Country.class);
ParameterExpression<Integer> p = cb.parameter(Integer.class);
q.select(c).where(cb.gt(c.get(Country_.population), p));
Requête JPQL objet
SELECT * FROM Country WHERE population >
<valeur>
Requête SQL textuelle
Mappage base objets Java
Base (SQL)
Objets
• Edition du fichier persistence.xml
• persistence.xml est automatiquement reconnu par l’implémentation
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" …>
<persistence-unit name="fc.Application.JPA">
<class>CustomerDatabase.Customer</class>
<class>CustomerDatabase.Invoice</class>
<class>CustomerDatabase.Item</class>
<class>CustomerDatabase.ItemPK</class>
<class>CustomerDatabase.Product</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:h2:C:\Users\Fred\Documents\repos\fcres\fc.Test\h2database" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="javax.persistence.ddl-generation" value="none" />
<property name="javax.persistence.ddl-generation.output-mode" value="both" />
<property name="javax.persistence.logging.level" value="FINE" />
</properties>
</persistence-unit>
</persistence>
Génération de classes (automatisée)
@Entity
@NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c")
public class Customer implements Serializable
{
private static final long serialVersionUID = 1L;
@Id
private int id;
private String city;
private String firstname;
private String lastname;
private String street;
//bi-directional many-to-one association to Invoice
@OneToMany(mappedBy="customer")
private List<Invoice> invoices;
Requêtage JPQL
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("fc.Application.JPA");
EntityManager em = emf.createEntityManager();
String str = "SELECT c FROM Customer c";
Query query = em.createQuery(str, Customer.class);
List<Customer> customers = (List<Customer>) query.getResultList();
for (Customer p : customers)
{
System.out.println("Nom = "+ p.getLastname());
}
em.close();
emf.close();
Requêtage Criteria Query
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = builder.createTupleQuery();
Root<Customer> c = q.from(Customer.class);
q.select(builder.tuple(
c.get(Customer_.firstname),
c.get(Customer_.lastname)));
List<Tuple> results = em.createQuery(q).getResultList();
Requêtage avec l’API Stream
• Le requêtage peut également se faire avec l’API Stream
• Le fournisseur JPQL peut fournir directement le support de stream:
String str = "SELECT c FROM Customer c";
TypedQuery<Customer> query = m_EM.createQuery(str, Customer.class);
Stream<Customer> customers = query.stream();
customers.
filter(c -> c.getFirstname().startsWith("S")).
forEach(c -> System.out.println(c.getFirstname()));
• En l’absence de support stream intégré au fournisseur JPQL,
il est possible d’appeler
query.getResultList().stream();
Requêtage avec l’API Stream
• Les clauses Stream ne sont pas converties en SQL
• Elles opèrent sur le résultat de la requête, « en mémoire »
• Données rapatriées en amont par JPA, stockées en mémoire, puis
(finalement) traitées par Stream
JPA
String str = "SELECT c FROM Customer c";
TypedQuery<Customer> query = m_EM.createQuery(str, Customer.class);
Stream<Customer> customers = query.stream();
Exécuté par la base elle-même (serveur)
Stream API
customers.
filter(c -> c.getFirstname().startsWith("S")).
forEach(c -> System.out.println(c.getFirstname()));
Exécuté par le code client
Visualisation du SQL généré
• persistence.xml:
Publicité
<property name="eclipselink.logging.level" value="FINE"/>
• Paramétrage similiaire pour d’autres implementations JPA
• Utile pour se familiariser avec JPQL si on connait déjà SQL
Mappage base/modèle objet
L’IDE fait respecter le mappage
Autocomplétion proposée par l’IDE (1/2)
Autocomplétion sur les types de la base
Autocomplétion sur l’interface
de requêtage
Autocomplétion proposée par l’IDE (2/2)
• Requêtes nommées
Requêtes nommées
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_2_0.xsd">
<named-query name="allSuppliers">
<query>SELECT s FROM Invoices s</query>
</named-query>
</entity-mappings>
Comparaison avec LINQ
• Prise en charge de la requête par le compilateur lui-même
• Autocomplétion disponible à l’intérieur de la requête
Un raytraceur en 1 seule expression LINQ
https://github.com/lukehoban/LINQ-raytracer
JINQ : Java INtegrated Query
• Développé par Ming-Chen Iu en 2015
• Tente de fournir les mêmes fonctionnalités que LINQ
• Même interface que la Stream API, mais transforme le code des
lambdas en code JPQL
• Le code source des lambdas est réinterprété ; il n’est jamais exécuté
• Stabilité ?
String str = "SELECT c FROM Customer c";
TypedQuery<Customer> query = m_EM.createQuery(str, Customer.class);
Stream<Customer> customers = query.stream();
customers.
filter(c -> c.getFirstname().startsWith("S")).
forEach(c -> System.out.println(c.getFirstname()));
Réécriture du code sous forme SQL
LINQ/JINQ
SQL
https://weblogs.asp.net/scottgu/linq-to-sql-part-3-querying-our-database
JINQ
Client
Serveur
XML
Base (SQL)
API objet
JPQL
API texte
JPQL
JINQ
Lourd à
écrire
Texte =
peu sûr
Facile à
écrire,
rapide
(serveur)
Résultat
Stream
Lent
(client)
JINQ
• https://blog.jooq.org/tag/jinq/ (Min-Cheng Iu)
Autres initiatives
Stab
• Langage dérivé de Java qui apporte les fonctionnalités de C#
• Dont LINQ
• Compile du bytecode JVM
• Abandonné en 2011
SBQL4J
• Comme Stab, mais avec une approche préprocesseur
• Pours (intégration toolchain) et contre (IDE…) de l’approche préprocesseur
• Abandonné en 2013
JOOQ
• Propriétaire et commercial
• Pas de syntaxe textuelle pure
• Simple et assez puissant
• https://www.jooq.org/
Speedment
• Fournisseur de données SGBD universel pour l’API Stream
• Lambda (forcément) exécutées côté client...
• Compense en reconstituant un datastore côté client (risqué…)
• Très intéressant pour se faire la main avec Stream
Travaux pratiques
• Mise en place d’un environnement SGBD
• Etude de l’environnement
• Code
• Fichiers de configuration
• Services rendus par l’environnement de développement (IDE) lui-même
• Expérimentations: SQL (rappels), JPQL, Stream API