Review code Java

Le problème exposé dans ce sujet a été résolu.

Voici des remarques pêle-mêle:

  • Tu utilises group 'org.example' dans ton fichier gradle alors que ton domaine est différent. Ca devrait être quelque chose comme fr.rayandfz
  • Tu définis des choses relatives JUnit dans ton fichier gradle mais je ne vois aucun test unitaire
  • Tu fais toute ta partie SQL "à la main" ce que personne ne fait dans des projets un tant soit peu développé. Hibernate est assez populaire mais si tu veux écrire ton SQL toi-même et avoir plus de contrôle, tu peux utiliser myBatis ou JOOQ. J’imagine que pour un programme aussi simple que celui-ci, c’est acceptable mais pour des projets plus massifs, c’est rare.
  • Je me demande si ça performe bien car tu exéctues les inserts un par un. Je ne sais plus comment Java gère ça exactement mais j’imagine qu’il va créer une transaction par statement. Tu pourrais sans doute changer ça pour plutôt faire un seul insert massif (je ne suis pas certain cela dit)
  • Tu définis ta configuration dans un fichier Java. Généralement, tu définis ça dans des fichiers .properties (dans le dossier resources) afin de ne pas devoir compiler ton code selon l’environnement sur lequel tu veux faire tourner ton programme.
  • Tes méthodes font beaucoup trop de choses. Il est recommandé de suivre le "Single Responsibility Principle" qui veut qu’une méthode fait une et une seule chose. Certains poussent ça à l’extrême et il faut faire attention à ce que ça reste lisible mais je trouve que tu devrais découper ton code en plusieurs méthodes.

Ce que tu fais, ce n’est pas de la programmation orienté objet et ça ne ressemble pas vraiment à du Java. Toutes tes méthodes sont static, ce qui est généralement l’opposé de ce qui est généralement recommandé. En Java typique, tu définis des classes (voir des records si tu utilises Java 16) pour représenter tes données (tu aurais donc une classe Post, une classe PostMeta, une classe Comment, une class CommentMeta). Puis tu peux avoir une classe Repository avec des méthodes "fetchPost" ou "insertPostMeta" ou ce genre de choses.

+2 -0

Bonjour,

Tu ne précise pas quelle version de Java tu cible, je vais donc me concentrer sur certains aspects de ton code.

Je vois aussi que tu n’utilise pas d’ORM (pour un si petit projet ça va encore, mais si ça grossit, il serait judicieux d’en utiliser un), on va donc rester dans ce cadre que tu t’es fixé.

Les duplications de code

Dans ton fichier Comments.java

A la place de :

            PreparedStatement st = twoConnection.prepareStatement("INSERT INTO " + Config.TWO_PREFIX + "comments (" +
                    "comment_id," +
                    "comment_post_id," +
                    "comment_author," +
                    "comment_author_email," +
                    "comment_author_url," +
                    "comment_author_ip," +
                    "comment_date," +
                    "comment_date_gmt," +
                    "comment_content," +
                    "comment_karma," +
                    "comment_approved," +
                    "comment_agent," +
                    "comment_type," +
                    "comment_parent," +
                    "user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");

            st.setString(1, rs.getString("comment_id"));
            st.setString(2, rs.getString("comment_post_id"));
            st.setString(3, rs.getString("comment_author"));
            st.setString(4, rs.getString("comment_author_email"));
            st.setString(5, rs.getString("comment_author_url"));
            st.setString(6, rs.getString("comment_author_ip"));
            st.setString(7, rs.getString("comment_date"));
            st.setString(8, rs.getString("comment_date_gmt"));
            st.setString(9, rs.getString("comment_content"));
            st.setString(10, rs.getString("comment_karma"));
            st.setString(11, rs.getString("comment_approved"));
            st.setString(12, rs.getString("comment_agent"));
            st.setString(13, rs.getString("comment_type"));
            st.setString(14, rs.getString("comment_parent"));
            st.setString(15, rs.getString("user_id"));

Je te conseille ça :

List<String> columns = Arrays.asList(
  "comment_id",
  "comment_post_id",
  "comment_author",
  "comment_author_email",
  "comment_author_url",
  "comment_author_ip",
  "comment_date",
  "comment_date_gmt",
  "comment_content"
  "comment_karma"
  "comment_approved"
  "comment_agent"
  "comment_type"
  "comment_parent",
  "user_id"
);
String stCols = String.join(",", columns);
String stParams = String.join(",", Collections.nCopies(columns.size(), "?"));

PreparedStatement st = twoConnection.prepareStatement("INSERT INTO " + Config.TWO_PREFIX + "comments ("+ stCols +") VALUES ("+stParams+")");
for (int i = 0; i < columns.size(); i++) {
  st.setString(i+1, rs.getString(columns.get(i)));
}

L’avantage principal (hormis le code plus court) est que tu évite de dupliquer tes noms de colonnes à deux endroits, ce qui le rend plus facile à faire évoluer.

Tu peux te servir de la même technique sur le traitement de commentmeta ainsi que dans ton fichier Post.java.

Fermeture des connexion

Tu ouvres des connexion avec des lignes telles que Connection firstConnection = DriverManager.getConnection(Config.FIRST_URL, Config.FIRST_USERNAME, Config.FIRST_PASSWORD); mais elle ne sont pas fermées quelque part.

Un simple firstConnection.close() à la fin te permet de pallier à ce problème.

L’envoi des requêtes par batch

Je prend l’exemple sur le fichier Comments.java, mais le problème existe aussi dans le fichier Post.java.

Pour insérer chaque ligne, je remarque que tu ouvres une nouvelle connexion, ce qui n’est pas très optimal. Tu pourrais envoyer tes requêtes par batchs.

Le code de ton fichier Comments.java ressemblerait à ceci (je l’écris à la main, donc des erreurs de syntaxes peuvent s’y glisser) si je prends en compte toutes les remarques du dessus.

public class Comments {
    public void insertData(Connection firstConnection, Connection twoConnection, String tableName, List<String> columns) {
        ResultSet rs = firstConnection.createStatement().executeQuery("SELECT * FROM " + Config.FIRST_PREFIX + tableName);
        
        String stCols = String.join(",", columns);
        String stParams = String.join(",", Collections.nCopies(columns.size(), "?"));
        PreparedStatement st = twoConnection.prepareStatement("INSERT INTO " + Config.TWO_PREFIX + tableName +" ("+ stCols +") VALUES ("+stParams+")");

        while (rs.next()) {
          for (int i = 0; i < columns.size(); i++) {
            st.setString(i+1, rs.getString(columns.get(i)));
          }
          st.addBatch(); // on ajoute dans la file
        }
        st.executeBatch(); // on execute une seule fois le batch 
        st.close()
    }

    public static void process() throws SQLException {
        Connection firstConnection = DriverManager.getConnection(Config.FIRST_URL, Config.FIRST_USERNAME, Config.FIRST_PASSWORD);
        Connection twoConnection = DriverManager.getConnection(Config.TWO_URL, Config.TWO_USERNAME, Config.TWO_PASSWORD);

        List<String> columns = Arrays.asList(
          "comment_id",
          "comment_post_id",
          "comment_author",
          "comment_author_email",
          "comment_author_url",
          "comment_author_ip",
          "comment_date",
          "comment_date_gmt",
          "comment_content"
          "comment_karma"
          "comment_approved"
          "comment_agent"
          "comment_type"
          "comment_parent",
          "user_id");

        List<String> metas = Arrays.asList(
          "meta_id",
          "comment_id",
          "meta_key",
          "meta_value");

        insertData(firstConnection, twoConnection, "comments", columns);
        insertData(firstConnection, twoConnection, "commentmeta", metas);

        twoConnection.close();
        firstConnection.close()

    }
}

Tu peux t’en inspirer pour factoriser aussi ton code de la classe Post qui s’appuierait sur la fonction insertData que écrite ci-dessus.

concernant le choix de l’ORM pour l’instant, j’essaie de m’entraîner en SQL mais je sais qu’il ne faut pas utiliser ce genre de cadre pour de vrai applications

rayandfz

J’ai très très envie de tempérer, en précisant que ce n’est pas une généralité, et que ça dépend notamment des langages que tu utilises.

En Java je ne suis pas un spécialiste et il semblerait qu’utiliser un ORM comme Hibernate soit la norme, mais dans d’autres technos (je pense par exemple à Python ou Go), ce n’est pas du tout automatique. J’ai même récemment eu à faire un chantier d’une bonne semaine pour virer l’ORM de mon projet au boulot (~20K lignes de code, en Go, donc pas un "petit truc"), ce qui m’a fait gagner sur tous les plans (complexité du code, performances, maintenabilité, légèreté des binaires).

Je pense qu’il faut se méfier de ce genre de choix qu’on fait un peu "en mode automatique", du moins de façon dogmatique. Utiliser un ORM n’est pas systématiquement le meilleur choix, et je pense justement que tu fais très bien de commencer ton projet sans. Le moment venu tu en essayeras un et tu pourras comparer, et te demander ce qu’il t’apporte, avant de faire ton choix.

+1 -0

Je pense que j’ai fait un petit abus de langage en parlant d’ORM. Je suis tout à fait d’accord qu’il faut bien réfléchir avant d’utiliser un vrai ORM tel qu’Hibernate car ça va simplifier certaines choses mais en complexifier beaucoup d’autres. Et au final, tu peux te retrouver à devoir fixer des problèmes dus à ton ORM plutôt qu’à ton SQL.

Mais il existe des frameworks un peu plus légers qui te permettent de garder le contrôle sur ton SQL tout en rendant ton code plus lisible et en se chargeant de toute la partie pénible (ouvrir et fermer des transactions, mapper des objets en variables pour tes requêtes, etc.). J’ai de l’expérience avec myBatis avec lequel tu écris ton SQL toi-même mais myBatis t’offre certaines simplifications. Pour Java, comme mentionné plus haut, je connais aussi JOOQ qui est dans la même lignée mais te permet d’écrire ton SQL "en Java" et qui te permet de faire du type-safe SQL (entre autres). Je ne l’ai jamais utilisé mais ça a l’air plutôt sympa.

Bref, je suis d’accord sur le fait qu’il faut faire attention avant de se lancer dans l’utilisation d’un ORM mais il existe des bons compromis qui permettent de nous faciiliter la vie sans perdre le contrôle du SQL sous-jacent.

@Migwel oui, c’est pareil en Go par exemple. On utilise des outils comme sqlx, plus légers, qui nous permettent d’écrire du SQL directement, et de mapper automatiquement les résultats des requêtes vers des structures typées, tout en s’abstenant de chercher à faire le R d’un ORM (collections, jointures automatiques, etc.). Dans mon cas c’est de ça que j’ai eu besoin dernièrement.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte