Mybatis est un framework de persistance renommé, conçu pour simplifier l'interaction avec les bases de données relationnelles en Java. Il agit comme un pont entre les objets Java et les bases de données, en encapsulant les détails complexes de JDBC (Java Database Connectivity) et en permettant aux développeurs de se concentrer sur l'écriture de requêtes SQL. L'objectif principal de Mybatis est de mapper les résultats des requêtes SQL directement vers des objets Java et vice-versa, facilitant ainsi la couche de persistance.
Qu'est-ce que la Persistence ?
La persistance fait référence à la capacité des données à survivre à l'exécution du programme qui les a créées. En d'autres termes, c'est le processus de stockage des données de manière à ce qu'elles puissent être récupérées et utilisées ultérieurement, même après que l'application ait été arrêtée et redémarrée. Sans persistance, toutes les données traitées par une application seraient perdues une fois celle-ci fermée.
- État transitoire : Les données existent uniquement en mémoire vive (RAM) pendant l'exécution du programme et sont perdues à l'arrêt.
- État persistant : Les données sont stockées sur un support durable (comme une base de données, un fichier sur disque) et restent disponibles.
Avantages de Mybatis par rapport à JDBC
L'utilisation directe de JDBC pour les opérations de base de données implique une quantité significative de code répétitif et sujet aux erreurs. Voici une comparaison simple pour illustrer les points faibles de JDBC et les avantages de Mybatis.
Exemple d'Interaction JDBC Brut
Considérons un exemple simple de connexion à une base de données MySQL et de récupération de données d'une table "produits" en utilisant JDBC.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId& <version>8.0.11</version>
</dependency>
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class ExempleJdbcBrut {
public static void main(String[] args) {
Connection connexionDB = null;
PreparedStatement stmtPrepare = null;
ResultSet resultats = null;
try {
// 1. Charger le pilote de la base de données
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. Obtenir une connexion via le gestionnaire de pilotes
connexionDB = DriverManager.getConnection("jdbc:mysql://localhost:3306/maBaseDeTest", "root", "votreMotDePasse");
// 3. Définir la requête SQL avec un paramètre (si nécessaire)
String sqlQuery = "SELECT id_produit, nom_produit FROM produits WHERE prix > ?";
// 4. Obtenir un statement préparé
stmtPrepare = connexionDB.prepareStatement(sqlQuery);
// 5. Définir les paramètres (ici, le prix minimum)
stmtPrepare.setDouble(1, 10.00); // Produits dont le prix est supérieur à 10.00
// 6. Exécuter la requête et obtenir le jeu de résultats
resultats = stmtPrepare.executeQuery();
// 7. Parcourir le jeu de résultats
while (resultats.next()) {
System.out.println("ID: " + resultats.getInt("id_produit") + ", Nom: " + resultats.getString("nom_produit"));
}
} catch (ClassNotFoundException e) {
System.err.println("Pilote JDBC introuvable : " + e.getMessage());
e.printStackTrace();
} catch (SQLException e) {
System.err.println("Erreur SQL lors de l'exécution de la requête : " + e.getMessage());
e.printStackTrace();
} finally {
// 8. Libérer les ressources dans l'ordre inverse de leur création
if (resultats != null) {
try {
resultats.close();
} catch (SQLException e) {
System.err.println("Erreur lors de la fermeture du ResultSet : " + e.getMessage());
}
}
if (stmtPrepare != null) {
try {
stmtPrepare.close();
} catch (SQLException e) {
System.err.println("Erreur lors de la fermeture du PreparedStatement : " + e.getMessage());
}
}
if (connexionDB != null) {
try {
connexionDB.close();
} catch (SQLException e) {
System.err.println("Erreur lors de la fermeture de la connexion : " + e.getMessage());
}
}
}
}
}
Les inconvénients majeurs de l'approche JDBC directe sont les suivants :
- Code en dur (Hardcoding) : Les informations de connexion à la base de données sont souvent directement intégrées dans le code, rendant la maintenance difficile et les modifications coûteuses.
- Gestion des ressources : La gestion manuelle de la fermeture des connexions, statements et resultsets est fastidieuse et source d'erreurs (fuites de ressources).
- Répétition : De nombreuses lignes de code sont répétées pour chaque opération CRUD (Create, Read, Update, Delete).
- Mapping manuel : La conversion des résultats du ResultSet en objets Java est manuelle et sujette aux erreurs.
- Performance : La création et la fermeture fréquentes de connexions ont un impact sur les performances.
Mybatis résout ces problèmes en externalisant la configuration de la base de données dans des fichiers de propriétés ou XML, en gérant automatiquement le pool de connexions et en mappant les requêtes SQL vers des méthodes d'interfaces, réduisant considérablement le code passe-partout.
Mise en Œuvre de Mybatis
1. Configuration de la Base de Données
Créons une base de données simple et une table pour notre exemple Mybatis.
CREATE DATABASE maBaseMybatis;
USE maBaseMybatis;
CREATE TABLE articles (
id_article INT PRIMARY KEY AUTO_INCREMENT,
titre VARCHAR(100) NOT NULL,
contenu TEXT
);
INSERT INTO articles (titre, contenu) VALUES
('Premier Article', 'Ceci est le contenu du premier article.'),
('Deuxième Article', 'Et voici le texte du deuxième article.');
2. Dépendances Maven
Ajoutez les dépendances nécessaires dans votre fichier pom.xml.
<properties>
<java.version>8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3. Classe Bean (Entité)
Créez une classe Java qui représente la structure de votre table articles.
package com.exemple.entites;
public class Article {
private Long idArticle;
private String titre;
private String contenu;
public Long getIdArticle() {
return idArticle;
}
public void setIdArticle(Long idArticle) {
this.idArticle = idArticle;
}
public String getTitre() {
return titre;
}
public void setTitre(String titre) {
this.titre = titre;
}
public String getContenu() {
return contenu;
}
public void setContenu(String contenu) {
this.contenu = contenu;
}
@Override
public String toString() {
return "Article{" +
"idArticle=" + idArticle +
", titre='" + titre + '\'' +
", contenu='" + contenu + '\'' +
'}';
}
}
4. Interface Mapper
Bien que non explicitement utilisé dans l'exemple de selectOne par ID, il est recommandé de définir une interface Mapper. Mybatis peut générer une implémentation pour cette interface.
package com.exemple.mappers;
import com.exemple.entites.Article;
public interface ArticleMapper {
Article obtenirParId(Long identifiant);
// Ajoutez d'autres méthodes comme inserer, mettreAJour, supprimer
}
5. Fichier XML du Mapper
Ce fichier XML contient les requêtes SQL mappées aux méthodes de l'interface Mapper ou utilisables directement avec SqlSession.
<?xml version="1.0" encoding="UTF-8"?>
<!-- L'espace de noms doit correspondre au nom de package et de l'interface Mapper -->
<mapper namespace="com.exemple.mappers.ArticleMapper">
<!-- Requête pour trouver un article par son ID -->
<select id="obtenirParId" parameterType="java.lang.Long" resultType="com.exemple.entites.Article">
SELECT id_article AS idArticle, titre, contenu
FROM articles
WHERE id_article = #{identifiant}
</select>
</mapper>
6. Fichier de Propriétés de la Base de Données
Stockez les informations de connexion à la base de données dans un fichier db.properties.
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/maBaseMybatis?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
db.username=root
db.password=votreMotDePasse
7. Fichier de Configuration Mybatis (mybatis-config.xml)
Ce fichier central configure l'environnement Mybatis, y compris la source de données et les fichiers de mapping.
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!-- Chargement des propriétés externes -->
<properties resource="db.properties"/>
<!-- Définition des environnements de base de données -->
<environments default="developpement">
<environment id="developpement">
<!-- Gestionnaire de transactions JDBC par défaut -->
<transactionManager type="JDBC"/>
<!-- Configuration de la source de données (pooling) -->
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<!-- Enregistrement des fichiers de mapping SQL -->
<mappers>
<mapper resource="com/exemple/mappers/ArticleMapper.xml"/>
<!-- Ou si l'interface et le XML sont dans le même répertoire, vous pouvez utiliser:
<mapper class="com.exemple.mappers.ArticleMapper"/>
-->
</mappers>
</configuration>
8. Classe de Test
Enfin, écrivez une classe de test JUnit pour vérifier le fonctionnement de Mybatis.
package com.exemple.tests;
import com.exemple.entites.Article;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
public class TestMybatis {
@Test
public void testRechercheArticleParId() {
String fichierConfigMybatis = "mybatis-config.xml";
Reader lecteurConfig = null;
SqlSession sessionSQL = null;
try {
// 1. Lire le fichier de configuration Mybatis
lecteurConfig = Resources.getResourceAsReader(fichierConfigMybatis);
// 2. Construire la fabrique de sessions SQL
SqlSessionFactory fabriqueSQL = new SqlSessionFactoryBuilder().build(lecteurConfig);
// 3. Ouvrir une session SQL
sessionSQL = fabriqueSQL.openSession();
// 4. Exécuter la requête mappée et obtenir l'objet Article
// Le premier argument correspond à 'namespace.id' du mapper XML
Article articleRecupere = sessionSQL.selectOne("com.exemple.mappers.ArticleMapper.obtenirParId", 1L);
// 5. Afficher le résultat
if (articleRecupere != null) {
System.out.println("Article trouvé : " + articleRecupere);
} else {
System.out.println("Aucun article trouvé avec l'ID spécifié.");
}
} catch (IOException e) {
System.err.println("Erreur lors de la lecture du fichier de configuration Mybatis : " + e.getMessage());
e.printStackTrace();
} finally {
// 6. Fermer la session SQL pour libérer les ressources
if (sessionSQL != null) {
sessionSQL.close();
}
if (lecteurConfig != null) {
try {
lecteurConfig.close();
} catch (IOException e) {
System.err.println("Erreur lors de la fermeture du lecteur de configuration : " + e.getMessage());
}
}
}
}
}