Bonjour,
J'ai fait de nombreuses recherches ces derniers jours, lu beaucoup de documentation et de forums, et j'avoue être un peu perdu.
Je travaille sur la partie serveur d'un projet de suivi de collaborateurs (pour commencer, une centaine d'utilisateurs qui pourrait s'étendre à plusieurs milliers).
La question des performances est donc importante.
J'utilise Spring, hibernate et postgresql côté serveur avec JSE 1.5. Le tout est déployé dans le tomcat de liferay. Pas de serveurs d'applications.
La partie serveur expose des services à la partie web. La démarcation des transactions est basée sur les méthodes exposées dans ces services. Chaque service utilise un ou plusieurs DAO (qui se trouvent être des beans au sens spring comme les services). Chaque Dao partage un entityManagerFactory.
Exemple de configuration (pour simplifier je n'ai mis que le dao et le service des collaborateurs mais le principe est le même pour les autres entity):
Dans ce contexte, je cherche à tester mes services. Le problème que j'ai rencontré semble ultra-connu mais malheureusement je ne m'en sors pas.
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
53
54
55
56
57
58
59
60
61
62
63
64
65 <!-- Data source --> <bean id="gfinetDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close" lazy-init="true"> <property name="driverClass"> <value>${jdbc.driver}</value> </property> <property name="jdbcUrl"> <value>${jdbc.url}</value> </property> <property name="user"> <value>${jdbc.user}</value> </property> <property name="password"> <value>${jdbc.password}</value> </property> </bean> <!-- Entity manager factory --> <bean id="gfinetEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="gfinetDataSource" /> <property name="persistenceUnitName" value="gfinet${emf.persistence-unit}" /> </bean> <!-- dao --> <bean id="gfinetCollaboratorDao" class="fr.gfi.gfinet.server.dao.CollaboratorDao"> <property name="entityManagerFactory"> <ref bean="gfinetEntityManagerFactory" /> </property> </bean> <!-- service --> <bean id="fr.gfi.gfinet.server.CollaboratorService" class="fr.gfi.gfinet.server.service.CollaboratorServiceImpl"> <property name="collaboratorDao"> <ref bean="gfinetCollaboratorDao" /> </property> <property name="competenceDao"> <ref bean="gfinetCompetenceDao" /> </property> <property name="competenceGroupDao"> <ref bean="gfinetCompetenceGroupDao" /> </property> <property name="expertizeDao"> <ref bean="gfinetExpertizeDao" /> </property> </bean> <!-- Transaction manager --> <bean id="gfinetTxManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="gfinetEntityManagerFactory" /> </bean> <!-- demarcation des transactions --> <aop:config> <aop:pointcut id="collaboratorServiceMethods" expression="execution(* fr.gfi.gfinet.server.CollaboratorService.*(..))" /> <aop:advisor advice-ref="collaboratorServiceTxAdvice" pointcut-ref="collaboratorServiceMethods" /> </aop:config> <tx:advice id="collaboratorServiceTxAdvice" transaction-manager="gfinetTxManager"> <tx:attributes> <tx:method name="get*" propagation="SUPPORTS" rollback-for="fr.gfi.gfinet.common.exception.ServiceException" /> <tx:method name="*" propagation="REQUIRED" rollback-for="fr.gfi.gfinet.common.exception.ServiceException" /> </tx:attributes> </tx:advice>
Je suis parti d'un LazyInitializationException. Je me suis alors rendu compte que le chargement tardif ne marchait plus en dehors des transactions,
c'est à dire une fois que le service à retourné l'objet, paradoxalement, au moment ou on en aurait besoin.
Pour illustrer mon problème voici deux exemples bêtes qui lèvent une exception :
Exemple 1 :
Exemple 2 :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 // Imaginons qu'un collaborateur possède des missions (lazy loading activée): col1 = collaboratorService.getCollaborator(10); col.getMissions(); // Le dernier appel lève une exception LazyInitializationException si la // collection n'a pas été initialisé dans le dao et donc dans la transaction // lorsqu'on avait une session.
Ces deux exceptions sont du même type. De manière générale les objets détachés ne sont plus "manipulables" en dehors d'une session hibernate.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 Collaborator col = new Collaborator (); collaboratorService.saveCollaborator(col); collaboratorService.deleteCollaborator(col); // Le delete lève une autre exception java.lang.IllegalArgumentException: // Removing a detached instance.
Pour résoudre ce problème je ne vois que deux solutions :
- étendre la transaction et donc la session à la partie web avec le pattern OpenSessionInViewFilter ou OpenSessionInViewInterceptor (ce qui ne me semble pas très propre vu que ces préoccupations incombent il me semble plutôt à la partie serveur et limite un peu l'indépendance de chaque partie). OU ...
- rattacher des objets détachés à une session.
Je ne peux pour l'instant pas utilise le pattern OSIV car je n'ai absolument pas la main sur la partie web (intégré dans liferay).
Je travaille dans des fichiers de test avec testng pour tester mes services. Je me dis donc que pour l'instant seule la deuxième solution peut me sauver.
Ce qui amène enfin (pardon pour la longueur) mes questions.
- Comment rattacher des objets quand on a pas de sessions puisque c'est spring qui crée les transactions puis les sessions ?
Tout ce que j'ai c'est un entityManagerFactory de type org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean. Comment obtenir la session qui à été utilisé par la transaction précédente ?
- Ou alors comment faut-il procéder pour ne plus avoir ce problème de session. D'ailleurs, je ne comprends pas pourquoi chaque transaction doit-avoir sa session et donc pourquoi un simple delete ne peut-il pas être effectué meme s'il est effectué depuis une transaction différente.
Je m'excuse pour la longueur des explications. En espérant que les survivants pourront éclairer ma lanterne.
Merci beaucoup.
Partager