Chargement...

Base de données et persistance

Bases de données

Les bases de données assure la persistance et la longévité des données.

Les propriétés essentielles des bases de données sont regroupées sous l'acronyme ACID :

  • Atomicité : toute transaction doit s'éxécuter totalement ou pas du tout
  • Consistance : la base assure sa propre intégrité
  • Isolation : la base gère la concurrence entre les transactions
  • Durabilité : la base assure la pérennité des données

Les bases de données apportent leur complexité mais c'est souvent un passage obligé.

Types de bases de données

Il existe différents types de base de données :

  • relationnelles : tables de données avec des relations entres elles (clés distantes).
  • hierarchiques : les données sont stoquées dans une structure arborescente.
  • orientées objets : visent à faciliter le passage objets <-> stockage.
  • XML : stoquage et interrogation native en XML.
  • orientées documents : stoquage d'objets avec des propriétés complexes.
  • ...

les charges grandissantes et la flexibilité recherchée ont poussé certaines de ces solutions hors de la compétition.

Les bases des données flexibles gagnent en terrain sur le web car elles sont plus adaptée aux modèles de données et aux charges importantes.

Ces bases sont aussi appelées bases NoSQL (mongoDB, cassandra et solr...)

Java DataBase Connectivity

La spécification JDBC date des premières années de java.


Le code client fait appel à une interface uniforme, quelle que soit la base de données.

La couche JDBC fait appel à un driver spécifique à la base de données.

Techniquement c'est une impémentation du pattern bridge.

Exemple JDBC

L'utilisation de JDBC est assez simple.

// chargement du driver
Class.forName("com.mysql.jdbc.Driver");

// création d'une connexion
String url = "jdbc:mysql://localhost/test";

Connection connection = DriverManager.getConnection(url, "username", "password");

// et une requête
Statement statement = connection.createStatement();

ResultSet rs = st.executeQuery("select * from table_name");

while (rs.hasNext()) {
    ...
}

La manipulation du SQL est rapidement verbeuse.

La libération des resources après utilisation et la gestion erreurs n'est pas simple.

Object Relational Mapping

Les bases de données relationnelles sont fortement implantées en entreprise.

Mais la connexion entre un modèle objet et une base de données relationnelles pose des problèmes récurrents.

On parle d'un problème d'impédance objet - relationnel.

La spécification JPA (Java Persistance API) définit une norme de compatibilité pour l'implémentation de librairies ORM (Object Relationnal Mapping) en Java.

Voici quelques librairies implémentant JPA :

  • JBoss Hibernate
  • Apache OpenJPA
  • Oracle Toplink

Hibernate est la plus utilisée.

Exemple : l'objet du domaine

Ce sont les classes qui représentent notre domain métier et qui doivent être persistées.

public class Event {
    private Long id;

    private String title;

    private Date date;

    public Long getId() { return id; }
    private void setId(Long id) { this.id = id; }

    public Date getDate() { return date; }
    public void setDate(Date date) { this.date = date; }

    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
}

Ici nous prenons en exemple un évènement simple avec 3 attributs.

Cet exemple est tiré de la documentation hibernate : The first Hibernate Application

Exemple : configuration

La configuration d'hibernate ressemble à la mise en place de JDBC.

Configuration cfg = new Configuration();

// dialect HSQL
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
// driver pour HSQL
cfg.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
// url de connexion
cfg.setProperty("hibernate.connection.url", "jdbc:hsqldb:hsql://localhost");
// username et password
cfg.setProperty("hibernate.connection.username", "sa");
cfg.setProperty("hibernate.connection.password", "");
// auto création des tables
cfg.setProperty("hibernate.hbm2ddl.auto", "update");

SessionFactory sessionFactory = cfg.buildSessionFactory();

Il est aussi possible de configurer hibernate via un fichier xml.

Exemple : annotations

Les annotations sont le moyen le plus simple de paramétrer la persistance.

@Entity
@Table(name = "events")
public class Event {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String title;

    @Column
    @Temporal(TemporalType.TIMESTAMP)
    private Date date;

    ...
}

On peut ajouter cette classe dans la configuration.

cfg.addAnnotatedClass(Event.class);

Exemple : utilisation

Une fois configuré, hibernate est simple a utiliser.

SessionFactory sessionFactory = ...
Session session = sessionFactory.openSession();

// save
Event event = new Event();
event.setTitle(title);
event.setDate(theDate);

session.save(event);

// select
List events = session.createQuery("from Event").list();

C'est un gain énorme par rapport à l'équivalent JDBC / SQL

Relations

Hibernate gère aussi les relations entre les entités.

Imaginons que la classe Event ait une relation vers une classe Source

La classe Event est en partie composée d'une classe Source partagée entre plusieurs évènements.

Avec les annotations, cela se définit comme suit.

public class Event {
    @ManyToOne(optional = false)
    private Source source

    ...
}

La référence vers la classe Source doit donc être remplie avant de persister un Event.

Cardinalités

Le mapping des associations entre les classes est une chose complexe même avec hibernate.

Il existe 4 cardinalités possibles pour les relations.

  • one to one : un à un
  • many to one : plusieurs vers un
  • one to many : un vers plusieurs
  • many to many : plusieurs à plusieurs

Hibernate se charge de persister et charger les objets.

Cependant, le nombre de ces relations rendent les choses complexes à gérer.

Cela impacte la également la performance.

Pour plus de détails, la documentation de hibernate sur le mapping d'assiociations est beaucoup plus complète.

Les limites

Hibernate n'est pas une librairie magique qui annihile la complexité.

Elle ne dédouane pas d'apprendre le SQL, les contraintes des bases de données relationnelles et de faire attention à la performance.

Il faut donc apprécier ce qu'apporte l'ORM sans penser qu'il est tout puissant.

`