From 281d88d8ae085bab622ad649943656e47779aff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=A3lie=20*?= <44349276+tetelie@users.noreply.github.com> Date: Sun, 1 Mar 2026 22:39:35 +0100 Subject: [PATCH] add hikari pool to avoid db connection timeout --- src/main/java/fr/tetelie/crawler/Crawler.java | 48 +++++------ .../fr/tetelie/crawler/DatabaseConfig.java | 82 ++++++++----------- src/main/resources/templates/index.html | 2 +- 3 files changed, 56 insertions(+), 76 deletions(-) diff --git a/src/main/java/fr/tetelie/crawler/Crawler.java b/src/main/java/fr/tetelie/crawler/Crawler.java index 6713a52..2e0ae67 100644 --- a/src/main/java/fr/tetelie/crawler/Crawler.java +++ b/src/main/java/fr/tetelie/crawler/Crawler.java @@ -1,9 +1,6 @@ package fr.tetelie.crawler; import io.github.cdimascio.dotenv.Dotenv; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @@ -12,47 +9,40 @@ import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling public class Crawler { - public static void main(String[] args) { - // Création des instances + // 1. Création des instances (Fidèle à ton code) new WebScrapper(); new DatabaseConfig(); - // Connexion à la base de donnée + // 2. Connexion initiale boolean isConnected = DatabaseConfig.getInstance().connect(); + if(!isConnected){ + System.err.println("Impossible de démarrer : BDD injoignable."); + return; + } - // Si pas de connexion à la base de donnée on s'arrête ici - if(!isConnected){return;}; - - + // 3. Setup Environnement et Spring Dotenv dotenv = Dotenv.configure().ignoreIfMissing().load(); dotenv.entries().forEach(entry -> System.setProperty(entry.getKey(), entry.getValue())); SpringApplication.run(Crawler.class, args); - - - // On request les prix de tous les produits et on les inject dans la bdd - //DatabaseConfig.getInstance().updateAllPrices(); - + // 4. Ta boucle infinie de 24h while (true) { - System.out.println("Début d'un cycle de mise à jour..."); - // On ajoute les images pour les nouveaux produits qui en ont pas encore + System.out.println("--- Début d'un cycle de mise à jour ---"); + + // Les méthodes récupèrent maintenant une connexion fraîche via le pool DatabaseConfig.getInstance().updatesAllMissingImages(); - - - // On request les prix de tous les produits et on les inject dans la bdd DatabaseConfig.getInstance().updateAllPrices(); System.out.println("Cycle terminé. Sommeil de 24 heures..."); - try { - // Attend 24 heures avant le prochain scan (en millisecondes) - Thread.sleep(24 * 60 * 60 * 1000); - } catch (InterruptedException e) { - break; - } + try { + // 24 heures de pause + Thread.sleep(24 * 60 * 60 * 1000); + } catch (InterruptedException e) { + System.err.println("Le programme a été interrompu."); + break; } } - - -} + } +} \ No newline at end of file diff --git a/src/main/java/fr/tetelie/crawler/DatabaseConfig.java b/src/main/java/fr/tetelie/crawler/DatabaseConfig.java index e98af9b..d149ffb 100644 --- a/src/main/java/fr/tetelie/crawler/DatabaseConfig.java +++ b/src/main/java/fr/tetelie/crawler/DatabaseConfig.java @@ -1,5 +1,7 @@ package fr.tetelie.crawler; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; import io.github.cdimascio.dotenv.Dotenv; import java.sql.*; @@ -7,45 +9,50 @@ import java.sql.*; public class DatabaseConfig { static DatabaseConfig instance; + private HikariDataSource dataSource; + // On garde ces variables pour l'affichage/log si tu veux private String dbUrl; private String dbUser; - private String dbPass; - - public Connection connection; public static DatabaseConfig getInstance() { return instance; - } public DatabaseConfig() { - instance = this; - // Charge le fichier .env Dotenv dotenv = Dotenv.load(); - // Récupère les variables dbUrl = dotenv.get("DB_URL"); dbUser = dotenv.get("DB_USER"); - dbPass = dotenv.get("DB_PASS"); + String dbPass = dotenv.get("DB_PASS"); + + // Configuration du Pool (HikariCP est inclus par défaut dans Spring Boot) + // Il gère la reconnexion automatique après les longues périodes d'inactivité + HikariConfig config = new HikariConfig(); + config.setJdbcUrl(dbUrl); + config.setUsername(dbUser); + config.setPassword(dbPass); + + config.setMaximumPoolSize(5); + config.setConnectionTimeout(30000); + config.setIdleTimeout(600000); + config.setMaxLifetime(1800000); + // Cette ligne est magique : elle teste la connexion avant de te la donner + config.setConnectionTestQuery("SELECT 1"); + + this.dataSource = new HikariDataSource(config); System.out.println("Database URL: " + dbUrl); System.out.println("Database User: " + dbUser); - - // Maintenant tu peux utiliser dbPass dans ton DriverManager.getConnection() - System.out.println("Configuration chargée avec succès !"); + System.out.println("Configuration et Pool chargés avec succès !"); } public boolean connect() { - System.out.println("Tentative de connexion à la base de données..."); - - try { - // On assigne directement à la variable de classe - this.connection = DriverManager.getConnection(dbUrl, dbUser, dbPass); - - if (this.connection != null && !this.connection.isClosed()) { - System.out.println("✅ SUCCÈS : Connexion établie avec brio !"); + System.out.println("Vérification de la connectivité base de données..."); + try (Connection conn = dataSource.getConnection()) { + if (conn != null && !conn.isClosed()) { + System.out.println("✅ SUCCÈS : La base de données est accessible !"); return true; } } catch (SQLException e) { @@ -53,43 +60,36 @@ public class DatabaseConfig { } return false; } + public void updateAllPrices() { String selectQuery = "SELECT id, link FROM products"; - String insertQuery = "INSERT INTO price_history (product_id, price, checked_at) VALUES (?, ?, CURRENT_TIMESTAMP)"; - try (PreparedStatement selectStmt = connection.prepareStatement(selectQuery); + // On demande une connexion au pool AU DÉBUT de la méthode + // Le try-with-resources la fermera (la rendra au pool) à la fin automatiquement + try (Connection connection = dataSource.getConnection(); + PreparedStatement selectStmt = connection.prepareStatement(selectQuery); ResultSet rs = selectStmt.executeQuery()) { while (rs.next()) { int id = rs.getInt("id"); String url = rs.getString("link"); - System.out.println("Analyse du prix pour l'ID : " + id + "..."); - // 3. Appel de ta fonction de scraping float currentPrice = WebScrapper.getInstance().requestPrice(url); - // On vérifie que le prix est valide (pas d'erreur de scraping) if (currentPrice > 0) { try (PreparedStatement insertStmt = connection.prepareStatement(insertQuery)) { insertStmt.setInt(1, id); insertStmt.setFloat(2, currentPrice); insertStmt.executeUpdate(); - System.out.println("✅ Nouveau prix enregistré : " + currentPrice + "€ pour l'ID " + id); } - } else { - System.err.println("⚠️ Impossible de récupérer le prix pour l'ID " + id); } - - // Un crawler poli attend toujours un peu entre deux requêtes Thread.sleep(2000); } - } catch (SQLException | InterruptedException e) { System.err.println("❌ Erreur lors de l'historisation des prix : " + e.getMessage()); - e.printStackTrace(); } } @@ -97,15 +97,14 @@ public class DatabaseConfig { String selectQuery = "SELECT id, link FROM products WHERE image_url IS NULL OR image_url = ''"; String updateQuery = "UPDATE products SET image_url = ? WHERE id = ?"; - - try (PreparedStatement selectStmt = connection.prepareStatement(selectQuery); + try (Connection connection = dataSource.getConnection(); + PreparedStatement selectStmt = connection.prepareStatement(selectQuery); ResultSet rs = selectStmt.executeQuery()) { while (rs.next()) { int id = rs.getInt("id"); String urlProduit = rs.getString("link"); - - System.out.println("Traitement de l'ID : " + id); + System.out.println("Traitement image pour l'ID : " + id); String imageUrl = WebScrapper.getInstance().requestImage(urlProduit); @@ -117,19 +116,10 @@ public class DatabaseConfig { System.out.println("Image mise à jour pour ID " + id); } } - - // Petit délai pour ne pas saturer le serveur cible Thread.sleep(1500); } - } catch (SQLException | InterruptedException e) { - e.printStackTrace(); + System.err.println("❌ Erreur lors de la mise à jour des images : " + e.getMessage()); } } - - - - - - -} +} \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 1100fb4..bff94e7 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -3,7 +3,7 @@
-