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 :

JPA & Spring & Spring-WS : impossible d'avoir une transaction [Data]


Sujet :

Spring Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 38
    Par défaut JPA & Spring & Spring-WS : impossible d'avoir une transaction
    Bonjour à tous, je suis débutant en Spring. Je tente de créer un web service permettant d’interagir avec une base de donnée.
    Pour cela je dispose de :
    - MySQL
    - Hibernate 3.6.6
    - Spring 3.0.5.RELEASE
    - Spring-WS 2.0.2.RELEASE
    - Tomcat 7

    J'ai configuré Spring pour utiliser JPA.

    spring.xml
    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
     
    <beans>
    	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" />
     
    	<bean id="persistenceUnitManager"
    		class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
    		<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
    		<property name="dataSources">
    			<map>
    				<entry key="localDataSource" value-ref="dataSource" />
    			</map>
    		</property>
    	</bean>
     
    	<bean id="entityManagerFactory"
    		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="persistenceUnitManager" ref="persistenceUnitManager" />
    	</bean>
     
    	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    		<property name="entityManagerFactory" ref="entityManagerFactory" />
    	</bean>
     
    	<tx:annotation-driven transaction-manager="transactionManager" />
     
    	<context:annotation-config />
    	<context:component-scan base-package="fr.company.compta.business" />
     
    </beans>
    persistence.xml
    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
     
    <persistence>
     
    	<persistence-unit name="compta-business" transaction-type="RESOURCE_LOCAL">
     
    		<provider>org.hibernate.ejb.HibernatePersistence</provider>
     
    		...
    		<class>fr.company.compta.business.data.entity.Thirdparty</class>
    		...
     
    		<properties>
    			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
    			<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
    			<property name="hibernate.connection.username" value="compta"/>
    			<property name="hibernate.connection.password" value="compta"/>
    			<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/compta"/>
    			<property name="hibernate.hbm2ddl.auto" value="validate" />
    			<property name="hibernate.show_sql" value="true" />
    		</properties>
     
    	</persistence-unit>
     
    </persistence>
    Mon point d'entrée est un web service, d'où la présence d'un web.xml

    web.xml
    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
     
    <web-app>
     
    	<display-name>Compta Business</display-name>
     
    	<context-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>classpath:/META-INF/spring.xml</param-value>
    	</context-param>
     
    	<servlet>
    		<servlet-name>spring-ws</servlet-name>
    		<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
    		<init-param>
    			<param-name>transformWsdlLocations</param-name>
    			<param-value>true</param-value>
    		</init-param>
    	</servlet>
     
    	<servlet-mapping>
    		<servlet-name>spring-ws</servlet-name>
    		<url-pattern>/*</url-pattern>
    	</servlet-mapping>
     
    	<listener>
    		<listener-class>
    			 org.springframework.web.context.ContextLoaderListener
    		</listener-class>
    	</listener>
     
    </web-app>
    Je tente d'appeler un service pour insérer un objet en base. Pour cela le endpoint appelle une classe service gérant les transactions, cette dernière appelle une DAO, qui elle appelle une DAO générique.
    Et bien sûr cette dernière utilise l'entityManager configuré.

    Endpoint : appel de "createThirdpartyRequest" via SOAP UI
    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
     
    @Endpoint
    public class BasicEndpoint {
     
    	private static final String NAMESPACE_URI = "http://company.fr/compta/schemas";
    	private XPath nameExpression;
     
    	private ThirdpartyService thirdpartyService;
     
    	@Autowired
    	public BasicEndpoint(ThirdpartyService thirdpartyService)
    			throws JDOMException {
    		this.thirdpartyService = thirdpartyService;
     
    		Namespace namespace = Namespace.getNamespace("x", NAMESPACE_URI);
     
    		nameExpression = XPath.newInstance("//x:Name");
    		nameExpression.addNamespace(namespace);
    	}
     
    	@PayloadRoot(namespace = NAMESPACE_URI, localPart = "CreateThirdpartyRequest")
    	public void createThirdpartyRequest(
    			@RequestPayload Element createThirdpartyRequest) throws Exception {
     
    		String name = nameExpression.valueOf(createThirdpartyRequest);
     
    		thirdpartyService.createThirdparty(name);
    	}
    }
    Service
    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
     
    @Service("ThirdpartyService")
    @Transactional
    public class ThirdpartyServiceImpl implements ThirdpartyService {
     
    	@Autowired
    	private ThirdpartyDao thirdpartyDao;
     
    	@Override
    	@Transactional(readOnly = false)
    	public void createThirdparty(String name) {
    		Thirdparty thirdparty = new Thirdparty(name);
    		thirdpartyDao.create(thirdparty);
    	}
    }
    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
     
    @Service("ThirdpartyDao")
    public class ThirdpartyDaoImpl implements ThirdpartyDao {
     
    	@Autowired
    	private GenericDao dao;
     
    	@Override
    	public void create(Thirdparty thirdparty) {
    		dao.create(thirdparty);
    	}
     
    	...
    }
    DAO générique
    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
     
    @Repository("GenericDao")
    @Transactional(propagation=Propagation.MANDATORY)
    public class GenericDaoImpl implements GenericDao {
     
    	@PersistenceContext
    	private EntityManager em;
     
    	@Override
    	@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
    	public <T> T create(T t) {
    		em.persist(t);
    		em.flush();
    		em.refresh(t);
    		return t;
    	}
     
    	...
    }

    Le WSDL est bien généré et j'appelle le service "CreateThirdpartyRequest" avec SoapUI en précisant un paramètre "name".

    Or une exception est générée :

    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
     
    javax.persistence.TransactionRequiredException: no transaction is in progress
            at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:792)
            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:597)
            at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
            at $Proxy240.flush(Unknown Source)
            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:597)
            at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
            at $Proxy232.flush(Unknown Source)
            at fr.company.compta.business.data.access.impl.GenericDaoImpl.create(GenericDaoImpl.java:25)
            at fr.company.compta.business.data.access.impl.ThirdpartyDaoImpl.create(ThirdpartyDaoImpl.java:21)
            at fr.company.compta.business.service.logic.impl.ThirdpartyServiceImpl.createThirdparty(ThirdpartyServiceImpl.java:25)
            at fr.company.compta.business.web.endpoint.BasicEndpoint.createThirdpartyRequest(BasicEndpoint.java:40)
            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:597)
            at org.springframework.ws.server.endpoint.MethodEndpoint.invoke(MethodEndpoint.java:132)
            at org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter.invokeInternal(DefaultMethodEndpointAdapter.java:229)
            at org.springframework.ws.server.endpoint.adapter.AbstractMethodEndpointAdapter.invoke(AbstractMethodEndpointAdapter.java:53)
            at org.springframework.ws.server.MessageDispatcher.dispatch(MessageDispatcher.java:231)
            at org.springframework.ws.server.MessageDispatcher.receive(MessageDispatcher.java:172)
            at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:88)
            at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:57)
            at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:222)
            at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
            at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
            at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
            at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
            at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
            at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
            at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
            at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:851)
            at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
            at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
            at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:257)
            at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
            at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1764)
            at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
            at java.lang.Thread.run(Thread.java:662)


    Je pense avoir mal configuré le transactionManager ou bien mal annoté mes classes et méthodes ... Qu'en pensez vous ?

    Merci à vous

  2. #2
    Membre expérimenté Avatar de aymen83
    Inscrit en
    Décembre 2007
    Messages
    271
    Détails du profil
    Informations forums :
    Inscription : Décembre 2007
    Messages : 271
    Par défaut
    bonjour,

    je pense qu'il te manque la configuration spring aop qui permet à spring de connaitre les annotations @Transactional. il faut consulter la doc spring pour voir comment ça se configure.

  3. #3
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 963
    Par défaut
    Citation Envoyé par aymen83 Voir le message
    bonjour,

    je pense qu'il te manque la configuration spring aop qui permet à spring de connaitre les annotations @Transactional. il faut consulter la doc spring pour voir comment ça se configure.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <tx:annotation-driven transaction-manager="transactionManager" />
    et
    @Transactional
    doivent suffire.

    sert si on utilise
    au lieu de @Transactional

    et
    [code]
    <aop:aspectj-autoproxy />
    [code]
    sert pour @AspectJ


    Par contre, des point suspects sont :
    a.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    <property name="dataSource" ref="dataSource" />
    de l'entityManagerFactory n'est pas utile puisque la dataSource est connue via le paramètre de persistenceUnit.

    b. l'utilisation d'un persistenceUnitManager dans sa version multiple datasources est à surveiller car il est connu que @Transactional ne fonctionne qu'avec une seule dataSource : voir les sources de Spring pour vérifier comment il réagit en présence d'une persistenceUnit configurée ainsi (c'est pour cela que l'option de configuration <tx> est souvent utilisée : pourvoir faire du transactionnel sur plusieurs sources de données…)

    c. le @Transactional sur les 2 couches DAO et Service peut engendrer des confusions à l'avenir : il y a un intérêt à choisir l'un ou l'autre, en plus vous mettez @Transactional(propagation=Propagation.MANDATORY) sur la couche DAO générique ce qui indique que vous exigez que ce soit l'appelant (la couche Service le plus souvent) qui démarre la transaction : donc gardez cette position partout et ne rajoutez pas du @Transactional sur le DAO pour combler un manque de la couche Service.

    d.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    @Transactional(propagation=Propagation.REQUIRED, readOnly=true)
    demander du readOnly sur une méthode qui s'appelle "create" est de toute évidence une contradiction sémantique…

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 38
    Par défaut
    Merci à vous pour ces réponses rapides et si précises !!!


    Pour les points suspects :

    a- Entièrement d'accord : j'ai enlevé la déclaration de la datasource dans le persistenceUnitManager.

    b- J'ai fait le ménage : cf spring.xml ci-dessous.

    c- Je garde le @Transactional uniquement sur la couche Service

    d- Oupsss c'est corrigé !


    Voici donc le spring.xml tout neuf :

    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
     
    <beans>
     
    	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" />
     
    	<bean id="persistenceUnitManager"
    			class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
    		<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" />
    	</bean>
     
    	<bean id="entityManagerFactory"
    		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<property name="persistenceUnitManager" ref="persistenceUnitManager" />
    	</bean>
     
    	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    		<property name="entityManagerFactory" ref="entityManagerFactory" />
    	</bean>
     
    	<tx:annotation-driven transaction-manager="transactionManager" />
     
    	<context:annotation-config />
    	<context:component-scan base-package="fr.company.compta.business" />
     
    </beans>
    Seulement voila, j'ai toujours encore et encore cette maudite exception qui indique que je n'ai pas de transactions en cours ...

    Or cette exception ne survient que si j'appelle la couche service du Endpoint via Soap UI ...

    Avec une fonction main() telle que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    public class Main {
     
    	public static void main(String[] args) {
    		ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring.xml");
    		BeanFactory factory = context;
    		ThirdpartyService service = (ThirdpartyService) factory.getBean("ThirdpartyService");
    		service.createThirdparty("Boulangerie");
    	}
     
    }
    ... là ça passe nickel, je vois bien la ligne "Boulangerie" apparaître en base.


    Donc j'en conclu que Spring-WS sème le trouble dans tout ça. Peut-être est-ce mon web.xml qui est mal configuré.

  5. #5
    Membre Expert
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 963
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 963
    Par défaut
    Citation Envoyé par Ernesto_X Voir le message
    Avec une fonction main() telle que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    public class Main {
     
    	public static void main(String[] args) {
    		ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring.xml");
    		BeanFactory factory = context;
    		ThirdpartyService service = (ThirdpartyService) factory.getBean("ThirdpartyService");
    		service.createThirdparty("Boulangerie");
    	}
     
    }
    ... là ça passe nickel, je vois bien la ligne "Boulangerie" apparaître en base.


    Donc j'en conclu que Spring-WS sème le trouble dans tout ça. Peut-être est-ce mon web.xml qui est mal configuré.
    clairement l'instance exécutée du WS n'a pas été instrumentée par Spring :

    a. soit que votre point d'entrée qui mène au WS n'a pas été instrumenté par Spring : il n'est pas considéré comme un bean parce que ni dans le XML ni annoté par @Service ou équivalent ou il est dans un package qui n'est pas scanné (donc pas dans le "base-package" - qui peut être une liste si nécessaire - et çà peut être à cause d'une bête faute de frappe dans son nom de package…)

    b. que pour une raison d'erreur de configuration, il y a 2 instances du WS qui co-existent, l'une créé par Spring, l'autre créée par votre code (via un new ou éq.) et cette dernière évidement n'est pas instrumentée, cependant si c'est un problème "classique" avec Axis, je ne l'ai pas encore vu avec Spring-WS.

    c. …

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Décembre 2009
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2009
    Messages : 38
    Par défaut
    CA Y EST CA MAAAARCHE !!!

    Le problème était trop bête en fait, voici ci-dessous la solution.


    AVANT :
    - dans mon spring.xml :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ...
    <context:component-scan base-package="fr.company.compta.business" />
    ...
    - dans mon spring-ws-servlet.xml (qui configure spring-ws) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ...
    <context:component-scan base-package="fr.company.compta.business" />
    ...
    J'avais mis le même base-package pour Spring et pour Spring-WS étant donné que les Endpoints et les couches Services/DAO sont dans celui-ci mais à un niveau inférieur. Du coup, j'ai juste eu à détailler les base packages ...


    APRÈS :
    - dans mon spring.xml :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ...
    <context:component-scan base-package="fr.company.compta.business.data" />
    <context:component-scan base-package="fr.company.compta.business.service" />
    ...
    - dans mon spring-ws-servlet.xml :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ...
    <context:component-scan base-package="fr.company.compta.business.web" />
    ...
    Et voila !!!


    En tout cas merci à tous d'avoir regardé ce topic. J'ai appris des choses, et j'espère que ceux qui auront le même problème trouverons ce post !

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

Discussions similaires

  1. [WD14] Impossible d'annuler une transaction
    Par tonioboss dans le forum WinDev
    Réponses: 2
    Dernier message: 19/10/2012, 12h01
  2. [Data] Impossible de rollbacker une transaction JDBC !
    Par Atatorus dans le forum Spring
    Réponses: 1
    Dernier message: 09/03/2010, 14h16
  3. Impossible d'avoir une font soulignée
    Par vincent.mbg dans le forum Tkinter
    Réponses: 8
    Dernier message: 21/01/2010, 09h51

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