AXOPEN

Projet JEE 7 sans hibernate ni JPA

Dans cet article, nous allons étudier la possibilité de créer un projet JEE 7 classique mais en supprimant la partie JPA / Hibernate.

Pourquoi se passer d’Hibernate?

JPA / Hibernate vient avec la promesse d’une simplication drastique de l’utilisation des bases de données avec un modèle ORM et une API de criteria sensée faciliter la vie des développeurs ainsi que la maintenance des applications. A l’usage, les choses se révèlent moins magiques que prévues et de nombreux problèmes viennent compliquer l’utilisation de cette technologie. Dans cette article, nous essaierons d’être le plus objectifs possible concernant JPA / Hibernate en listant dans un premier temps tous les avantages et gains apportés par cette technologie.

Les gains de JPA / Hibernate sont rééls au début d’un projet :

  • Mapping objet-relationnel
  • Indépendance vis-à-vis du SGBD
  • Gestion des transactions
  • Possibilité de ne pas écrire les requêtes SQL avec l’API Criteria
  • Chargement de dépendances fonctionnelles en LazyLoading
  • Sauvegardes automatiques (requêtage explicite et implicite)

Ces fonctionnalités sont intéressantes en début de projet et permettent de créer rapidement une application de type CRUD (Create, Read, Update, Delete). Mais plus le projet avance et plus les problèmes se font sentir. Même avec de l’expérience sur cette technologie, certains problèmes s’avèrent extrêmement complexes à résoudre. Voici une petite liste des problèmes classiques que l’on rencontre dans un projet avec Hibernate et en particulier dans un projet Web.

Les problèmes majeurs d’Hibernate / JPA :

  • La LazyInitializationException (sans parler de HibernateSessionFilter)
  • Mapping ManyToMany
  • Problèmes de performance (multiplication de requêtes pour la synchronisation d’Hibernate avec la base de données)
  • Requêtes SQL spécifiques (impossibilité de réaliser un « UNION » ou un « SELECT * FROM (SELECT …) » avec l’API Criteria)
  • Difficultés de maintenance
  • Lisibilité des requêtes avec l’API Criteria
  • Gestion des transactions
  • Requêtes implicites

A cela se rajoutent des contraintes d’ordre organisationnel :

  • Formation des développeurs : même si vos développeur maîtrisent parfaitement SQL, l’utilisation de JPA n’a rien à voir
  • Difficulté de debug : chaque problème est très complexe à résoudre et il n’est pas rare que 60 % du temps du projet soit passé à résoudre des problèmes de persistence
  • Performance : réaliser de bonnes performances avec JPA relève tout simplement du miracle, même si l’utilisation de cache améliore sensiblement les choses
  • Evolutivité : une montée de version d’Hibernate est un enfer

En fin de compte, il apparaît assez clairement avec l’expérience et le recul que les quelques facilités que JPA / Hibernate est susceptible de proposer sont insignifiantes en face de l’infinité de bugs et de difficultés de développement qu’il impose. Cette prise de conscience réalisée, le retour aux fondamentaux devient inéluctable.

Réalisation d’un projet sans Hibernate / JPA

Nous allons maintenant voir comment réaliser un projet en gardant un découpage MVC (qui lui reste une très bonne idée).

Création de manager

Il est possible de s’injecter directement avec l’annotation @Resource la datasource sur lequel nous allons réaliser nos requêtes (PreparedStatement). Il est préférable d’utiliser des PreparedStatement pour éviter les injections SQL. Qui plus est les PreparedStatement peuvent aussi être mis en cache.

Avec l’apparition de la syntaxe try-with-resources, il devient facile de créer sa connexion et d’être certain que tout sera bien fermé comme il le faut.

Avec ce type de manager, on gagne en lisibilité, maintenance, performance, avec toutefois une concession sur l’écriture des PreparedStatements.

L’injection de la datasource est gérée par le serveur d’application (ici Wildfly 8).

Injection de la Datasource avec @Resource

On garde ainsi la gestion du pool de connexions par le serveur, ce qui est très performant.

@Resource(mappedName = "java:/datasource")
private DataSource mDataSource;

Création d’une méthode de listing

Voyons maintenant comment écrire une methode permettant de lister les commandes pour un client. L’ouverture de la connexion et du PreparedStatement directement dans le try permet de ne pas se soucier de la fermeture et permet ainsi de gagner en lisibilité.

public List listCommandeForClientId(int pIdClient) {
    String lSelectCommandeQuery = "SELECT  commande_id FROM client WHERE client_id=? ORDER BY id DESC";

    List lResults = new ArrayList();
    try (Connection lConnection = mDataSource.getConnection();
            PreparedStatement lPreparedStatement = lConnection
                    .prepareStatement(lSelectCommandeQuery);)
                    {
        lPreparedStatement.setInt(1, pIdClient);
        ResultSet rs = lPreparedStatement.executeQuery();
        Commande lCommande = null;
        while (rs.next()) {
            lCommande = new Commande();
            lCommande.setId(rs.getInt("commande_id"));
            lResults.add(lCommande);
        }
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
    return lResults;
}

Conclusion

Le retour au JDBC est facilité par l’injection de la datasource et la gestion des pools de connexions par le serveur d’application, ainsi que par la structure de langage try-with-resources. De ce fait, cette technologie devient simple à implémenter, tout en étant clairement la plus performante dans le monde Java. L’ensemble des contraintes négatives apportées par JPA / Hibernate disparaissent, tandis qu’à l’inverse on regrette peu des avantages de ce dernier. On concèdera tout de même que les mappings champs à champs sont un peu fastidieux. Mais à chaque instant on sait ce que l’on a dans ses objets d’encapsulation de données, et l’on ne passe que les requêtes nécessaires. L’abandon d’Hibernate au profit de JDBC est donc bien bénéfique.
jpa