IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Spring Java Discussion :

Gestion des transactions avec @Transactional [Framework]


Sujet :

Spring Java

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut Gestion des transactions avec @Transactional
    Bonjour,

    Il m'est arrivé d'utiliser l'annotation @Transactional au boulot, mais je dois avouer qu'en fait je ne maîtrise pas vraiment l'impact dans la gestion des transactions par Spring.

    Actuellement, j'ai une application où je gère manuellement mes transactions :
    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
     
    org.hibernate.Session session = getSession();
    try {
    	org.hibernate.Transaction tx = session.beginTransaction();
    	try {
    		session.save(entity);
    		tx.commit();
    	}
    	catch (HibernateException e) {
    		tx.rollback();
    		throw new TechnicalException(e);
    	}
    }
    finally {
    	session.close();
    }
    Ce que je voudrais savoir , c'est si l'utilisation de @Transactional au niveau de la couche service ou DAO me dispenserait de faire des rollbacks et des commits, voire me dispenserait complètement de gérer les transactions hibernate ?

    En gros, est-ce que je pourrais avoir un code comme ça ? :
    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
     
    @Transactional(rollbackFor=TechnicalException.class)
    public void save(Object entity) {
    	org.hibernate.Session session = getSession();
    	try {
    		try {
    			session.save(entity);
    		}
    		catch (HibernateException e) {
    			throw new TechnicalException(e);
    		}
    	}
    	finally {
    		session.close();
    	}
    }

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 274
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 274
    Par défaut
    C'est effectivement l'intérêt de cette annotation.
    Néanmoins, il vaudrait mieux utiliser cette annotation au niveau d'une couche de services placée au dessus de la couche Dao, sinon l'intérêt de la transaction est limité.

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Ok merci pour ta réponse

  4. #4
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Bonjour,

    J'ai appliqué l'annotation @Transactional à mon DAO, et j'obtiens une erreur lorsque je fais un test unitaire en utilisant HSQLDB.

    Voici le code :
    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
     
    @Transactional(rollbackFor=Exception.class)
    public void inscrire(ClientModel client) throws BusinessException, TechnicalException {
    	[...]
     
    	Session session = getSession();
    	try {
    		try {
    			Long id = (Long) session.save(entity);
    			if (id == null) {
    				throw new TechnicalException("Result id null after save entity. Rollback transaction.");
    			}
    			client.setId(id);
    		}
    		catch (HibernateException e) {
    			throw new TechnicalException(e);
    		}
    	}
    	finally {
    		session.close();
    	}
    }
    Voici la conf spring :
    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
     
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
     
    	<context:annotation-config />
    	<tx:annotation-driven transaction-manager="transactionManager"/>
     
    	<bean id="hsqldb" class="webapp.db.HsqlDbServerTest" />
     
    	<bean id="resolver"
    		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="prefix" value="/WEB-INF/jsp" />
    	</bean>
     
    	<!-- Debut Hibernate Configuration -->
    	<bean id="dataSource"
    		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
    		<property name="url" value="jdbc:hsqldb:file:storage/test_db" />
    		<property name="username" value="sa" />
    		<property name="password" value="" />
    	</bean>
     
    	<bean id="sessionFactory"
    		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
    				<prop key="hibernate.show_sql">true</prop>
    				<prop key="hibernate.connection.pool_size">1</prop>
    				<prop key="hibernate.current_session_context_class">thread</prop>
    				<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
    			</props>
    		</property>
    		<property name="annotatedClasses">
    			<list>
    				<value>business.dao.entity.client.ClientEntity</value>
    			</list>
    		</property>
    	</bean>
     
    	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    		<property name="sessionFactory" ref="sessionFactory" />
    	</bean>
    	<!-- Fin Hibernate Configuration -->
     
    </beans>
    Et voici la stack trace :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    business.exception.TechnicalException: org.hibernate.HibernateException: save is not valid without active transaction
    at business.dao.impl.ClientDaoImpl.inscrire(ClientDaoImpl.java:58) //il s'agit de la méthode annotée @Transactional
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:592)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:319)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy23.inscrire(Unknown Source)
    Je pense que j'ai dû louper un truc dans ma configuration, mais après avoir cherché dans tous les sens sur Internet, je n'ai pas trouvé de piste probante.

  5. #5
    Rédacteur/Modérateur
    Avatar de andry.aime
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    8 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Ile Maurice

    Informations forums :
    Inscription : Septembre 2007
    Messages : 8 391
    Par défaut
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Transaction transaction=session.beginTransaction();
    Long id = (Long) session.save(entity);
    transaction.commit();
    A+.

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Bonjour, merci pour ta réponse.

    En fait ma question va dans le fil de la discussion. Si tu remontes dans le topic, j'avais demandé si l'utilisation de @Transactional permettait d'éviter de gérer soit-même les transactions. Ce à quoi fr1man m'a répondu que oui.

    Est-ce que j'ai mal compris ? Et dans ce cas, quel serait l'intérêt de l'annotation @Transactional ?

  7. #7
    Rédacteur/Modérateur
    Avatar de andry.aime
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    8 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Ile Maurice

    Informations forums :
    Inscription : Septembre 2007
    Messages : 8 391
    Par défaut
    Salut,

    En effet tu as raison, j'ai lu la discussion qu'à partir de ton dernier poste, je pense que tu as seulement oublié quelque chose dans le fichier de configuration:
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    <context:component-scan base-package="******"/>

    A+.

  8. #8
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    J'avais bien ajouté le package dans lequel se situe mon DAO, mais ça ne change rien.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <context:component-scan base-package="business.dao.impl" />

  9. #9
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 274
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 274
    Par défaut
    Ton DAO est il bien annoté pour en faire un bean spring ?

  10. #10
    Membre chevronné
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Par défaut
    Citation Envoyé par fr1man Voir le message
    Ton DAO est il bien annoté pour en faire un bean spring ?
    Ce serait bien effet d'avoir tout le code du DAO pour se faire une meilleure idée de ce qui se passe.

    Mais à première vue, il me semble que c'est la façon dont la session Hibernate est obtenue qui ne permet pas à celle-ci de reconnaître qu'il y a une transaction en cours.

    Il faut utiliser getCurrentSession() au lieu de getSession() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Session session = sessionFactory.getCurrentSession();

  11. #11
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Voici mon DAO :
    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
     
    @Transactional
    @Repository
    public class EtablissementDaoImpl implements IEtablissementDao {
     
    	@Autowired
    	private SessionFactory sessionFactory;
     
    	private Session getSession() {
    		return sessionFactory.getCurrentSession();
    	}
     
    	@Transactional(rollbackFor=Exception.class)
    	public void inscrire(EtablissementModel establishment) throws BusinessException, TechnicalException {
    		[...]
     
    		Session session = getSession();
    		try {
    			try {
    				Long id = (Long) session.save(entity);
    				if (id == null) {
    					throw new TechnicalException("Result id null after save entity. Rollback transaction.");
    				}
    				establishment.setId(id);
    			}
    			catch (HibernateException e) {
    				throw new TechnicalException(e);
    			}
    		}
    		finally {
    			session.close();
    		}
    	}
     
    }

  12. #12
    Membre chevronné
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Par défaut
    OK, je crois que je sais d'où vient le problème. C'est que tu fermes la session avant même la fin de la transaction.

    Il faut laisser Hibernate gérer tout seul le cycle de vie de la session:
    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
     
    @Transactional(rollbackFor=Exception.class)
    public void inscrire(ClientModel client) throws BusinessException, TechnicalException {
    	[...]
     
    	Session session = getSession();
     
    	try {
    		session.save(client);
     
    	}
    	catch (HibernateException e) {
    		throw new TechnicalException(e);
    	}
     
    }
    En plus, j'ai supprimé ton test sur id == null, ça n'a pas trop de sens étant donné que l'identifiant pourrait n'être généré qu'au commit de la transaction, lorsque la session est flushée. Ceci dans le cas où cet identifiant est auto-incrémenté (cas d'une base MySQL par exemple).

  13. #13
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Merci pour ta réponse manblaizo.

    J'ai essayé de supprimer la fermeture de la session, mais ça ne change rien. Et de fait, l'erreur survient à l'appel de session.save(), c'est-à-dire avant le finally.

  14. #14
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 274
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 274
    Par défaut
    Et ton dao, tu l'utilises comment ?

  15. #15
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Je l'utilise dans un test unitaire qui échoue avec la stack trace fournie dans le premier post :
    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
     
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="/spring-mvc-test.xml")
    public class EtablissementDaoTest {
     
    	@Autowired
    	private IEtablissementDao etablissementDao;
     
    	@Autowired
    	private HsqlDbServerTest hsqldb;
     
    	@Before
    	public void start() {
    		hsqldb.start();
    	}
     
    	@After
    	public void shutdown() {
    		hsqldb.shutdown();
    	}
     
    	@Test
    	public void inscrire() throws BusinessException, TechnicalException {
    		EtablissementModel etablissement = new EtablissementModel();
    		etablissement.setName("etablissement");
     
    		etablissementDao.inscrire(etablissement);
     
    		Assert.assertNotNull(etablissement.getId());
    	}
     
    }

  16. #16
    Membre chevronné
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Par défaut
    Etrange ! Le code m'a l'air correct pourtant.

    Sinon, pour nous aider à diagnostiquer, tu utilises quelle version de Spring ? Je vois que ton fichier de config Spring déclare des schémas pour la version 2.5:
    http://www.springframework.org/schem...-beans-2.5.xsd

    Mais si tu as bien une version 3.x, pourrais-tu changer ça en
    http://www.springframework.org/schem...-beans-3.0.xsd ou autre 3.1 ?

    Je ne suis pas certain que le problème vienne de là, mais on ne sait jamais.

  17. #17
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    J'utilise la version 3.1.4.

    J'ai essayé en changeant la version du xsd, mais sans succès.

    Sinon, je suppose que Spring utilise l'AOP pour gérer les transactions via @Transactional. Est-ce qu'il n'y a pas une config à spécifier pour cela. J'ai importé Spring AOP dans mes dépendances maven, mais est-ce qu'il n'y a pas une bidouille à faire en plus ?

  18. #18
    Membre chevronné
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    365
    Détails du profil
    Informations personnelles :
    Localisation : Maroc

    Informations forums :
    Inscription : Janvier 2006
    Messages : 365
    Par défaut
    Je crois que j'ai trouvé !!!

    Il faudrait enlever la propriété hibernate.current_session_context_class de ta définition du bean sessionFactory. Apparemment, Spring et Hibernate s'emmêlent les pinceaux quand tu spécifies ce paramètre.

    Il faudrait donc supprimer la ligne: <prop key="hibernate.current_session_context_class">thread</prop>

    C'est bien expliqué ici:
    http://forum.springsource.org/showth...903#post213903

  19. #19
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    YESSSSS !!!

    Bravo manblaizo !!

    Merci mille fois, je n'aurais jamais eu l'idée de fouiller dans ce sens.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. DBCC SHRINKFILE - gestion des journaux de transaction
    Par Mothership dans le forum Administration
    Réponses: 11
    Dernier message: 09/03/2010, 16h36
  2. [vb.net]Gestion des exceptions avec les web services
    Par mvr dans le forum Windows Forms
    Réponses: 2
    Dernier message: 05/12/2005, 22h41
  3. Gestion des factures avec interbase
    Par AlexB59 dans le forum Bases de données
    Réponses: 1
    Dernier message: 25/10/2005, 12h05
  4. Gestion des cookies avec Indy 10
    Par membrax dans le forum Web & réseau
    Réponses: 5
    Dernier message: 21/09/2005, 18h44
  5. Class de gestion des images avec rotation
    Par Johnny Boy dans le forum MFC
    Réponses: 1
    Dernier message: 03/05/2005, 11h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo