Bonjour,
J'ai un gros volume de grappe de données que je dois sauvegarder en base. Pour autant, si la sauvegarde d'une grappe plante, le but est de ne pas faire planter la sauvegarde des autres grappes.
Mon idée de départ était d'utiliser le traitement par lot d'hibernate avec des savepoints. Mais après lecture de la doc et des specs, Hibernate ne supporte pas cette fonctionnalité de JDBC.
J'ai alors 3 solutions :
solution 1 : j'effectue un commit à chaque sauvegarde de grappe.
-> ce n'est plus du traitement par lot !
-> si une grappe plante cela ne m'empêche pas de sauvegarder les autres...
solution 2 : j'effectue le commit à la fin
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 // connection JDBC depuis un POOL Connection conn = DbManager.getConnection(); // session hibernate à partir de la connection JDBC Session session = HibernateUtil.getSession(conn); for ( Grappe grappe : grappes ) { try { // début de la transaction tx = session.beginTransaction(); // sauvegarde de ma grappe d'objets dans la session save(session, grappe); // fin de la transaction tx.commit(); } catch (Throwable t) { if ( tx != null ) tx.rollback(); } session.clear(); } session.close();
-> traitement par lot ok.
-> si une grappe plante tout plante...
solution 3 : mélange hibernate - JDBC
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 // connection JDBC depuis un POOL Connection conn = DbManager.getConnection(); // session hibernate à partir de la connection JDBC Session session = HibernateUtil.getSession(conn); Transaction tx = null; try { // début de la transaction tx = session.beginTransaction(); int savepointCount = 0; for ( Grappe grappe : grappes ) { // sauvegarde de ma grappe d'objets dans la session save(session, grappe); // envoie des inserts vers la base session.flush(); // nettoyage de la session // -> évite un OutOfMemoryException dans le cas d'un gros volume de données session.clear(); } // fin de la transaction tx.commit(); } catch (Throwable t) { if ( tx != null ) tx.rollback(); } finally { session.close(); }
L'idée est de construire ma session Hibernate avec une connection JDBC déjà établie.
-> j'effectue mon traitement par lot avec un commit à la fin.
-> à l'intérieur de la boucle, j'effectue un savepoint sur la connection JDBC, si l'envoie de mes requêtes sur une grapppe plante, rollback de la connection JDBC vers le dernier savepoint.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 // connection JDBC depuis un POOL Connection conn = DbManager.getConnection(); // session hibernate à partir de la connection JDBC Session session = HibernateUtil.getSession(conn); Transaction tx = null; try { // début de la transaction tx = session.beginTransaction(); int savepointCount = 0; for ( Grappe grappe : grappes ) { // savepoint sur la connection JDBC directement avant chaque sauvegarde de clients String savepointName = "save" + savepointCount; Savepoint currentSavepoint = conn.setSavepoint(savepointName); try { // sauvegarde de ma grappe d'objets dans la session save(session, grappe); // envoie des inserts vers la base session.flush(); } catch (HibernateException ex) { // en cas d'erreur rollback vers le dernier flush réussi conn.rollback(currentSavepoint); System.out.println(ex.getCause().getMessage()); } savepointCount++; // nettoyage de la session // -> evite un OutOfMemoryError dans le cas d'un gros volume de données. // -> en cas de rollback évite aussi des problèmes d'incohérences entre la session et la base. session.clear(); } // fin de la transaction tx.commit(); } catch (Throwable t) { if ( tx != null ) tx.rollback(); } finally { session.close(); }
Si au niveau perf c'est bon, je pense partir sur la première solution.
Je suis quand même attiré par la troisième (que j'ai testée et qui fonctionne).
Le seul risque que j'ai relevé est d'avoir une incohérence entre ma session hibernate et la base.
Ex :
J'ai une grappe qui plante à un certain moment. Le rollback se fait sur l'ensemble de la grappe donc pas de soucis d'incohérence au niveau base où je pourrais avoir qu'une partie de la grappe sauvée. Le problème est que je peux avoir des objets qui restent en mémoire au niveau de ma session hibernate...
--> je pense minimiser ce risque en nettoyant ma session à chaque itération (session.clear()).
Mes questions :
- Est-ce politiquement correct d'attaquer la couche JDBC et Hibernate en même temps ?
- En plus de celui déjà relevé, avez-vous identifié d'autres risques dans la troisième solution ?
Merci pour votre lecture et vos réponses.
Partager