Évolution des Technologies de Persistance Java : De JDBC à MyBatis-Plus

La persistance des données est une exigence fondamentale dans le développement d'applications d'entreprise Java. Cet article retrace l'évolution des technologies de persistance Java, depuis le JDBC de base, en passant par le populaire MyBatis, jusqu'au puissant MyBatis-Plus. Grâce à des analyses détaillées du code source et des comparaisons, nous visons à aider les développeurs à comprendre en profondeur les principes de mise en œuvre, les avantages, les inconvénients et les scénarios d'application de ces trois technologies.

  1. JDBC Natif : La Pierre Angulaire des Opérations de Base de Données

1.1 Architecture Noyau de JDBC

JDBC (Java Database Connectivity) est l'API standard pour l'accès aux bases de données en Java. Elle se compose des éléments clés suivants :

  • DriverManager : Gère les pilotes de base de données.
  • Connection : Objet de connexion à la base de données.
  • Statement / PreparedStatement : Interfaces pour l'exécution des requêtes SQL.
  • ResultSet : Objet représentant le résultat d'une requête.

1.2 Implémentation CRUD Complète

1.2.1 Gestion de la Connexion à la Base de Données


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class DatabaseConnector {
   private static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase?serverTimezone=UTC";
   private static final String DB_USER = "dbuser";
   private static final String DB_PASSWORD = "dbpassword";
   
   // Chargement du pilote (optionnel avec JDBC 4.0+)
   static {
       try {
           // Assurez-vous que le pilote MySQL est dans votre classpath
           Class.forName("com.mysql.cj.jdbc.Driver"); 
       } catch (ClassNotFoundException e) {
           throw new RuntimeException("Échec du chargement du pilote de base de données", e);
       }
   }
   
   /**
    * Obtient une connexion à la base de données.
    * @return Objet Connection.
    * @throws SQLException En cas d'échec de l'obtention de la connexion.
    */
   public static Connection getConnection() throws SQLException {
       return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
   }
   
   /**
    * Ferme les ressources de connexion.
    * @param conn La connexion.
    * @param stmt Le statement.
    * @param rs Le result set.
    */
   public static void closeResources(Connection conn, Statement stmt, ResultSet rs) {
       try {
           if (rs != null) rs.close();
           if (stmt != null) stmt.close();
           if (conn != null) conn.close();
       } catch (SQLException e) {
           // Gérer l'exception ou la logger
           e.printStackTrace(); 
       }
   }
}
   

1.2.2 Implémentation des Opérations de Requête


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class DataFetcher {
   
   /**
    * Récupère un utilisateur par son ID.
    * @param userId L'ID de l'utilisateur.
    * @return L'objet User, ou null s'il n'est pas trouvé.
    */
   public User fetchUserById(long userId) {
       String querySql = "SELECT user_id, name, age, email_address FROM users WHERE user_id = ?";
       Connection connection = null;
       PreparedStatement preparedStatement = null;
       ResultSet resultSet = null;
       User foundUser = null;
       
       try {
           connection = DatabaseConnector.getConnection();
           preparedStatement = connection.prepareStatement(querySql);
           preparedStatement.setLong(1, userId);
           resultSet = preparedStatement.executeQuery();
           
           if (resultSet.next()) {
               foundUser = new User();
               foundUser.setId(resultSet.getLong("user_id"));
               foundUser.setFullName(resultSet.getString("name"));
               foundUser.setYearsOld(resultSet.getInt("age"));
               foundUser.setContactEmail(resultSet.getString("email_address"));
           }
       } catch (SQLException e) {
           e.printStackTrace(); // Log this properly
       } finally {
           DatabaseConnector.closeResources(connection, preparedStatement, resultSet);
       }
       return foundUser;
   }
   
   /**
    * Récupère tous les utilisateurs en utilisant try-with-resources.
    */
   public List<user> fetchAllUsers() {
       String queryAllSql = "SELECT user_id, name, age, email_address FROM users";
       List<user> userList = new ArrayList<>();
       
       try (Connection connection = DatabaseConnector.getConnection();
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(queryAllSql)) {
           
           while (resultSet.next()) {
               User user = new User();
               user.setId(resultSet.getLong("user_id"));
               user.setFullName(resultSet.getString("name"));
               user.setYearsOld(resultSet.getInt("age"));
               user.setContactEmail(resultSet.getString("email_address"));
               userList.add(user);
           }
       } catch (SQLException e) {
           e.printStackTrace(); // Log this properly
       }
       return userList;
   }
}
   </user></user>

1.2.3 Implémentation des Opérations de Mise à Jour


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

public class DataModifier {
   
   /**
    * Met à jour les informations d'un utilisateur.
    * @param user L'objet User mis à jour.
    * @return Le nombre de lignes affectées.
    */
   public int modifyUser(User user) {
       String updateSql = "UPDATE users SET name = ?, age = ?, email_address = ? WHERE user_id = ?";
       int affectedRows = 0;
       
       try (Connection connection = DatabaseConnector.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(updateSql)) {
           
           preparedStatement.setString(1, user.getFullName());
           preparedStatement.setInt(2, user.getYearsOld());
           preparedStatement.setString(3, user.getContactEmail());
           preparedStatement.setLong(4, user.getId());
           
           affectedRows = preparedStatement.executeUpdate();
       } catch (SQLException e) {
           e.printStackTrace(); // Log this properly
       }
       return affectedRows;
   }
   
   /**
    * Insère plusieurs utilisateurs en une seule transaction.
    * @param users La liste des utilisateurs à insérer.
    * @return Les nombres de lignes affectées pour chaque insertion.
    */
   public int[] batchInsertUsers(List<user> users) {
       String insertSql = "INSERT INTO users(name, age, email_address) VALUES (?, ?, ?)";
       
       try (Connection connection = DatabaseConnector.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(insertSql)) {
           
           connection.setAutoCommit(false); // Désactiver l'auto-commit pour gérer la transaction
           
           for (User user : users) {
               preparedStatement.setString(1, user.getFullName());
               preparedStatement.setInt(2, user.getYearsOld());
               preparedStatement.setString(3, user.getContactEmail());
               preparedStatement.addBatch(); // Ajouter à la pile de commandes
           }
           
           int[] batchResults = preparedStatement.executeBatch(); // Exécuter toutes les commandes en lot
           connection.commit(); // Valider la transaction
           return batchResults;
       } catch (SQLException e) {
           e.printStackTrace(); // Log this properly
           // Rollback en cas d'erreur (non implémenté ici pour la concision)
           return new int[0]; 
       }
   }
}
   </user>

1.3 Analyse des Avantages et Inconvénients de JDBC

  • Avantages : API standard, haute performance car accès direct, flexibilité maximale avec SQL arbitraire.
  • Inconvénients : Code répétitif important (boilerplate code), gestion manuelle des connexions et transactions, couplage fort entre SQL et Java, gestion manuelle des exceptions et ressources, mappage manuel des résultats.
  1. MyBatis : Un Pont Élégant entre SQL et Java

2.1 Architecture Noyau de MyBatis

MyBatis simplifie les opérations JDBC via les composants suivants :

  • SqlSessionFactory : Fabrique pour créer des SqlSession.
  • SqlSession : Interface principale pour exécuter les opérations CRUD.
  • Executor : Exécuteur des requêtes SQL.
  • MappedStatement : Encapusle les informations de la requête SQL et du mappage.
  • TypeHandler : Gère la conversion entre les types Java et JDBC.

2.2 Configuration et Mappage MyBatis

2.2.1 Exemple de Fichier de Configuration

mybatis-config.xml :


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
   <settings>
       <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- Convertit les noms de colonnes (snake_case) en camelCase -->
       <setting name="jdbcTypeForNull" value="OTHER"/>
   </settings>
   
   <typeAliases>
       <typeAlias type="com.example.model.UserProfile" alias="UserProfile"/>
   </typeAliases>
   
   <environments default="development">
       <environment id="development">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
               <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mydatabase?serverTimezone=UTC"/>
               <property name="username" value="dbuser"/>
               <property name="password" value="dbpassword"/>
           </dataSource>
       </environment>
   </environments>
   
   <mappers>
       <mapper resource="mappers/UserProfileMapper.xml"/>
   </mappers>
</configuration>
   

2.2.2 Exemple de Fichier Mapper XML

UserProfileMapper.xml :


<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.example.mapper.UserProfileMapper">
   <resultMap id="userProfileResultMap" type="UserProfile">
       <id property="userId" column="user_id"/>
       <result property="username" column="name"/>
       <result property="ageProfile" column="age"/>
       <result property="contactEmail" column="email_address"/>
   </resultMap>
   
   <select id="findProfileById" resultMap="userProfileResultMap">
       SELECT user_id, name, age, email_address FROM users WHERE user_id = #{identifier}
   </select>
   
   <insert id="addProfile" parameterType="UserProfile" useGeneratedKeys="true" keyProperty="userId">
       INSERT INTO users(name, age, email_address) VALUES(#{username}, #{ageProfile}, #{contactEmail})
   </insert>
   
   <update id="updateProfile" parameterType="UserProfile">
       UPDATE users SET name=#{username}, age=#{ageProfile}, email_address=#{contactEmail} WHERE user_id=#{userId}
   </update>
   
   <delete id="removeProfile" parameterType="long">
       DELETE FROM users WHERE user_id = #{idValue}
   </delete>
</mapper>
   

2.3 Analyse du Code Source Noyau de MyBatis

2.3.1 Processus de Création de SqlSession


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 java.io.IOException;
import java.io.InputStream;

public class MyBatisSessionManager {
   
   private static SqlSessionFactory sessionFactory;
   
   static {
       try {
           String configResource = "mybatis-config.xml";
           InputStream inputStream = Resources.getResourceAsStream(configResource);
           sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
       } catch (IOException e) {
           throw new RuntimeException("Configuration MyBatis introuvable ou invalide", e);
       }
   }
   
   /**
    * Ouvre une nouvelle session SqlSession.
    * @return Une instance de SqlSession.
    */
   public static SqlSession openSession() {
       return sessionFactory.openSession();
   }
   
   public static void main(String[] args) {
       // Utilisation de try-with-resources pour garantir la fermeture de la session
       try (SqlSession session = openSession()) {
           // Obtenir un proxy pour l'interface Mapper
           UserProfileMapper userMapper = session.getMapper(UserProfileMapper.class);
           
           // Exemple d'appel d'une méthode de mappage
           UserProfile profile = userMapper.findProfileById(1L); 
           System.out.println("Profil trouvé : " + profile);
       }
   }
}
   

2.3.2 Implémentation du Proxy de Mapper

MyBatis utilise le pattern Proxy pour lier les appels de méthode de l'interface Mapper à l'exécution SQL :


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// Pseudo-code simplifié pour l'illustration
public class MyBatisMapperProxy<T> implements InvocationHandler, Serializable {
   
   private final SqlSession sqlSessionContext;
   private final Class<T> mapperInterfaceType;
   private final Map<Method, MyBatisMapperMethod> methodCache = new ConcurrentHashMap<>();
   
   public MyBatisMapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
       this.sqlSessionContext = sqlSession;
       this.mapperInterfaceType = mapperInterface;
   }
   
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       // Gestion des méthodes héritées de Object
       if (Object.class.equals(method.getDeclaringClass())) {
           return method.invoke(this, args);
       }
       
       // Récupération ou création de la logique de méthode MyBatis
       final MyBatisMapperMethod mapperMethod = cachedOrConstructMapperMethod(method);
       
       // Exécution de la requête SQL via le SqlSession
       return mapperMethod.invoke(sqlSessionContext, args);
   }
   
   private MyBatisMapperMethod cachedOrConstructMapperMethod(Method method) {
       // Utilisation d'un cache pour éviter de recréer les objets MyBatisMapperMethod
       return methodCache.computeIfAbsent(method, 
           m -> new MyBatisMapperMethod(mapperInterfaceType, m, sqlSessionContext.getConfiguration()));
   }
}
   

2.3.3 Flux d'Exécution SQL


// Pseudo-code simplifié pour l'illustration
public class DefaultSqlSession implements SqlSession {
   // ...
   private final Executor sqlExecutor;
   private final Configuration configuration;
   
   @Override
   public <T> List<T> selectList(String statementId, Object parameter) {
       try {
           MappedStatement mappedStatement = configuration.getMappedStatement(statementId);
           // D�l�gation de l'exécution au niveau Executor
           return sqlExecutor.query(mappedStatement, wrapParameter(parameter), 
                                    RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
       } catch (Exception e) {
           throw ExceptionFactory.wrapException("Erreur lors de l'exécution de la requête : " + e, e);
       }
   }
   // ... autres méthodes CRUD
}

public class SimpleExecutor implements Executor { // Ou CachingExecutor
   // ...
   @Override
   public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
       // ... logique d'exécution et de création du ResultSet ...
       // Appelle le JDBC driver pour exécuter la requête SQL préparée.
       Statement stmt = prepareStatement(ms.getBoundSql(parameter));
       // ... traitement du ResultSet et transformation en liste d'objets ...
       return results; 
   }
   // ...
}
   

2.4 Analyse des Avantages et Inconvénients de MyBatis

  • Avantages : Séparation SQL/Java, mappage automatique, support SQL dynamique, Caching (niveaux 1 et 2), système de plugins pour l'extension.
  • Inconvénients : Nécessite d'écrire SQL et configurations, SQL complexe toujours manuel, pagination nécessite des plugins, générateur de code basique.
  1. MyBatis-Plus : La Boîte à Outils d'Amélioration pour MyBatis

3.1 Caractéristiques Principales de MyBatis-Plus

  • Mapper Générique : Méthodes CRUD courantes intégrées.
  • Constructeur de Conditions : API chaînée pour construire des requêtes.
  • Générateur de Code : Génération automatique des classes Entity, Mapper, Service.
  • Plugin de Pagination : Support de la pagination physique intégrée.
  • Plugin d'Analyse de Performance : Affiche le temps d'exécution des requêtes SQL.
  • Plugin de Verrouillage Optimiste : Support de l'annotation @Version.

3.2 Configuration et Utilisation de MyBatis-Plus

3.2.1 Configuration avec Spring Boot


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.example.repository") // Scan pour les interfaces Mapper
public class MyBatisPlusConfig {
   
   @Bean
   public MybatisPlusInterceptor mybatisPlusInterceptor() {
       MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
       // Ajout du plugin de pagination
       interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
       // Ajout du plugin de verrouillage optimiste
       interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
       // D'autres plugins peuvent être ajoutés ici (ex: Ligne d'entreprise)
       return interceptor;
   }

   // Optionnel : Si vous utilisez des champs de remplissage automatique (ex: createTime)
   // @Bean 
   // public GlobalConfig globalConfig() {
   //     GlobalConfig globalConfig = new GlobalConfig();
   //     globalConfig.setMetaObjectHandler(new MyMetaObjectHandler()); // Votre implémentation de MetaObjectHandler
   //     return globalConfig;
   // }
}
   

3.2.2 Définition de la Classe Entité


import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;

@Data // Annotation Lombok pour générer getters/setters, etc.
@TableName("app_users") // Nom de la table correspondante
public class AppUser {
   
   @TableId(value = "user_identifier", type = IdType.ASSIGN_ID) // Clé primaire auto-générée (ou assignée)
   private Long identifier;
   
   @TableField("user_name") // Correspondance du nom de champ
   private String name;
   
   private Integer age; // Pas besoin de @TableField si le nom correspond à la colonne (après transformation)
   
   @TableLogic // Marqueur pour la suppression logique
   private Integer deleted; 
   
   @Version // Pour le verrouillage optimiste
   private Integer revision;
   
   @TableField(fill = FieldFill.INSERT) // Champ rempli à l'insertion
   private Date creationTimestamp;
   
   @TableField(fill = FieldFill.INSERT_UPDATE) // Champ rempli à l'insertion et mise à jour
   private Date lastModifiedTimestamp;
}
   

3.3 Analyse du Code Source Noyau de MyBatis-Plus

3.3.1 Implémentation du Mapper Générique


// Interface fournie par MyBatis-Plus
public interface BaseMapper<T> extends MyBatisMapper<T> {
   
   int insert(T entity);
   int deleteById(@Param(Constants.ID) Serializable id);
   int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
   T selectById(@Param(Constants.ID) Serializable id);
   List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
   // ... beaucoup d'autres méthodes CRUD ...
}

// La logique d'exécution est gérée par des proxies et des injecteurs SQL
// Pseudo-code simplifié
public class BaseMapperImpl<T> implements BaseMapper<T> {
   // ...
   @Override
   public int insert(T entity) {
       // Utilisation des méthodes de base MyBatis pour exécuter l'instruction INSERT
       return sqlSession.insert("com.example.repository.BaseMapper.insert", entity);
   }
   
   @Override
   public T selectById(Serializable id) {
       // Le SQL est généré dynamiquement ou via le mapping XML/annotations
       return sqlSession.selectOne("com.example.repository.BaseMapper.selectById", id);
   }
   // ... implémentation pour les autres méthodes ...
}
   

3.3.2 Implémentation du Constructeur de Conditions


// Classe abstraite de base pour les constructeurs de conditions
public abstract class AbstractWrapper<T, Children extends AbstractWrapper<T,Children>> 
   implements Wrapper<T> {
   
   protected final List<ISqlSegment> sqlSegments = new ArrayList<>();
   protected Map<String, Object> paramNameValuePairs = new LinkedHashMap<>(); // Stocke les paramètres
   
   // Méthode pour ajouter une condition (ex: égalité)
   protected final Children addCondition(boolean condition, String column, SqlKeyword keyword, Object value) {
       if (condition) {
           // Ajoute le segment SQL et le paramètre correspondant
           sqlSegments.add(new SqlSegment(String.format("%s%s?", column, keyword.getSqlSegment())));
           paramNameValuePairs.put(column, value); // Le nom du paramètre peut être généré dynamiquement
       }
       return typedThis; // Retourne l'instance actuelle pour le chaînage
   }
   
   public Children eq(String column, Object value) {
       return addCondition(true, column, SqlKeyword.EQ, value);
   }

   public Children like(String column, String value) {
        return addCondition(true, column, SqlKeyword.LIKE, "%" + value + "%");
   }
   
   public String getSqlString() {
       // Assemble les segments SQL pour former la requête finale
       // ... logique d'assemblage ...
       return sqlSegments.stream().map(ISqlSegment::getSqlSegment).collect(Collectors.joining(" "));
   }
}
   

3.3.3 Implémentation du Plugin de Pagination


// Annotation indiquant l'interception des méthodes d'Executor
@Intercepts({
   @Signature(type = Executor.class, method = "query", 
             args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PaginationInterceptor implements Interceptor { // Implémente Interceptor au lieu de InnerInterceptor pour simplicité ici
   
   @Override
   public Object intercept(Invocation invocation) throws Throwable {
       // Détecter si une requête de pagination est en cours
       Object[] args = invocation.getArgs();
       MappedStatement ms = (MappedStatement) args[0];
       Object parameter = args[1];
       RowBounds rowBounds = (RowBounds) args[2];

       if (parameter instanceof IPage) { // Vérifie si le paramètre est une instance de IPage
           IPage<?> page = (IPage<?>) parameter;
           
           // 1. Générer et exécuter la requête COUNT pour obtenir le total
           String countSql = buildCountSql(ms.getBoundSql(parameter).getSql());
           // ... exécuter la requête COUNT via une nouvelle MappedStatement ...
           long totalCount = executeCountQuery(ms, countSql, parameter); 
           page.setTotal(totalCount);

           // 2. Générer la requête de pagination (LIMIT / OFFSET)
           String paginatedSql = buildPaginationSql(ms.getBoundSql(parameter).getSql(), page);
           
           // Modifier le BoundSql pour utiliser la requête paginée
           resetBoundSql(ms.getBoundSql(parameter), paginatedSql);
       }
       
       // Continuer l'exécution de la requête originale
       return invocation.proceed();
   }
   
   // Méthodes auxiliaires pour construire COUNT SQL, exécuter COUNT, construire pagination SQL, réinitialiser BoundSql...
   // ...
}
   

3.4 Analyse des Avantages et Inconvénients de MyBatis-Plus

  • Avantages : Réduction drastique du code répétitif, constructeur de conditions puissant, pagination et verrouillage optimiste intégrés, générateur de code complet, conserve toutes les fonctionnalités de MyBatis.
  • Inconvénients : Les SQL complexes nécessitent toujours un fichier XML, courbe d'apprentissage plus élevée que MyBatis seul, les SQL générés automatiquement peuvent parfois manquer d'optimisation.
  1. Comparaison des Technologies et Recommandations de Sélection

Caractéristique JDBC MyBatis MyBatis-Plus
Efficacité de Développement Faible Moyenne Élevée
Performance Élevée Moyenne à Élevée Moyenne à Élevée
Flexibilité Maximale Élevée Moyenne à Élevée
Courbe d'Apprentissage Faible Moyenne Moyenne à Élevée
Support Communautaire Standard Fort Fort
Scénarios d'Usage Performance extrême / Besoins spécifiques Contrôle SQL précis / Projets complexes Développement rapide CRUD / Systèmes standards

Recommandations de Sélection :

  • Pour des exigences de performance absolues ou des interactions très spécifiques avec la base de données : JDBC.
  • Pour un contrôle fin sur le SQL, une bonne maintenabilité et des requêtes complexes : MyBatis.
  • Pour accélérer le développement d'applications CRUD standards avec une grande efficacité : MyBatis-Plus.
  1. Points de Connaissance Supplémentaires

5.1 Technologies de Pool de Connexions

  • HikariCP : Performant et largement utilisé.
  • Druid : Open-source par Alibaba, offre des fonctionnalités de monitoring.
  • C3P0 : Une autre option populaire.

5.2 Transactions Distribuées

  • XA / Two-Phase Commit (2PC) : Standard mais peut être coûteux.
  • TCC (Try-Confirm-Cancel) : Modèle de compensation.
  • Saga Pattern : Séquence de transactions locales avec compensation.
  • Seata : Framework open-source pour les transactions distribuées (par Alibaba).

5.3 Comparaison des Frameworks ORM

Framework Avantages Inconvénients
Hibernate / JPA ORM complet, abstraction élevée, productivité Performance potentiellement inférieure, complexité pour requêtes avancées
MyBatis Contrôle SQL, bonne performance, flexible Nécessite l'écriture de SQL
MyBatis-Plus Productivité ORM-like, fonctionnalités riches Abstraction plus forte, SQL généré

5.4 Recommandations d'Optimisation de Performance

  • Utilisation judicieuse du caching (MyBatis L1/L2, cache applicatif).
  • Opérations en batch plutôt que des appels individuels répétés.
  • Éviter le problème N+1 avec des jointures ou des requêtes optimisées.
  • Conception d'index appropriés sur la base de données.
  • Configuration adéquate des pools de connexions.

Étiquettes: JDBC MyBatis MyBatis-Plus Persistance Java ORM

Publié le 15 août à 20h20