Comment configurer Spring pour que JPA (Hibernate) et JDBC (JdbcTemplate ou MyBatis) partagent la même transaction
j'ai une seule source de données, J'utilise Spring 3.0.3, Hibernate 3.5.1 comme fournisseur JPA et J'utilise MyBatis 3.0.2 Pour certaines requêtes et mon application tourne sur Tomcat 6. J'ai un HibernateDAO et un MyBatisDAO, quand j'appelle les deux de la même méthode qui est annoté avec @Transactional il semble qu'ils ne partagent pas la même transaction, ils obtiennent des connexions différentes.
Comment puis-je les faire faire?
j'ai essayé d'obtenir une connexion de DataSourceUtils.getConnection (dataSource) et je reçois celui qui est utilisé par MyBatis ce qui est étrange, je pensais que le problème était dans MyBatis config et il ne peut pas utiliser JpaTransactionManager. Même appeler plusieurs fois Datasuceutils.getConnection donne toujours la même connexion, ce qui est correct.
après quelques recherches sur Google, j'ai essayé spring-instrument-classloader de tomcat (bien que je ne sache pas si tomcat l'utilise vraiment:))
demande partiellecontexte
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:META-INF/mybatis/mybatis-config.xml" />
</bean>
partielle mybatis config
<settings>
<setting name="cacheEnabled" value="false" />
<setting name="useGeneratedKeys" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
partielle de la persévérance.xml
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
3 réponses
j'ai trouvé la solution ici: quel gestionnaire de transactions devrais-je utiliser pour jbdc template en utilisant JPA ?
J'utilise JpaTransactionManager et pas DataSourceTransactionManager.
JavaDoc http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html
Cette transaction manager prend également en charge l'accès direct à la source de données dans une transaction (c.-à-d. code JDBC simple travaillant avec la même source de données) . Cela permet de mixer les services qui accèdent à JPA et les services qui utilisent JDBC (sans être au courant de JPA)! Le code d'Application doit s'en tenir au même modèle de recherche de connexion simple qu'avec DataSourceTransactionManager (i.e. DataSourceUtils.getConnection (javax.SQL.DataSource) ou en passant par une TransactionAwareDataSourceProxy). noter que ce nécessite une configuration JpaDialect spécifique au fournisseur.
après que j'ai ajouté jpaVendorAdapter à mon entityManagerFactory config tout fonctionne, à la fois jdbcctemplate query et MyBatis fonctionne dans la même transaction comme prévu. Basé sur le JavaDoc je suppose qu'un jpaDialect devrait suffire mais il est 4 heures du matin ici donc je ne vais pas essayer que maintenant :)
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
</bean>
</property>
</bean>
Je n'ai pas MyBatis dans le mix, mais comme tewe a suggéré juste ajouter le jpaDialect au gestionnaire des transactions fait le travail aussi.
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
essayez d'utiliser:
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
qui fonctionne directement au niveau JDBC. Toutes les abstractions de persistance (JPA/iBatis et JdbcTemplate
) utilisent finalement JDBC, vous devez donc gérer les transactions au niveau commun le plus élevé.
dans votre cas, vous utilisez JpaTransactionManager
qui traite les transactions via javax.persistence.EntityTransaction
abstraction. De toute évidence, iBatis n'est pas au courant de la transaction de L'app, et il est donc probable qu'elle travaille en dehors de celle-ci.
vous n'avez pas besoin d'un chargeur de classe/instrumentation magique, devrait juste fonctionner.