ejemplos - Cómo configurar Spring para hacer que JPA(Hibernate) y JDBC(JdbcTemplate o MyBatis) compartan la misma transacción
spring data (3)
Tengo un solo dataSource, uso Spring 3.0.3, Hibernate 3.5.1 como proveedor JPA y uso MyBatis 3.0.2 para algunas consultas y mi aplicación se ejecuta en Tomcat 6. Tengo un HibernateDAO y un MyBatisDAO, cuando llamo a ambos del mismo método que está anotado con @Transactional parece que no comparten la misma transacción, obtienen diferentes conexiones.
¿Cómo puedo hacer que hagan?
Intenté obtener una conexión de DataSourceUtils.getConnection (dataSource) y obtuve la que usa MyBatis, lo que es extraño. Pensé que el problema estaba en la configuración de MyBatis y no puede usar JpaTransactionManager. Incluso llamando varias veces a DataSoruceUtils.getConnection siempre da la misma conexión, lo cual está bien.
Después de buscar en Google, probé el cargador de clases de spring-instrument-tomcat (aunque no sé si tomcat realmente lo usa :))
partial applicationContext
<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>
configuración parcial mybatis
<settings>
<setting name="cacheEnabled" value="false" />
<setting name="useGeneratedKeys" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
parcial persistence.xml
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
He encontrado la solución aquí: ¿Qué gestor de transacciones debería usar para la plantilla JBDC cuando uso JPA?
Estoy usando JpaTransactionManager y no DataSourceTransactionManager.
JavaDoc http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html
Este administrador de transacciones también admite el acceso directo a DataSource dentro de una transacción (es decir, código JDBC simple que funciona con el mismo DataSource) . Esto permite mezclar servicios que acceden a JPA y servicios que usan JDBC simple (¡sin tener conocimiento de JPA)! El código de la aplicación debe seguir el mismo patrón simple de búsqueda de conexión que con DataSourceTransactionManager (es decir, DataSourceUtils.getConnection (javax.sql.DataSource) o pasar por TransactionAwareDataSourceProxy). Tenga en cuenta que esto requiere que se configure un JpaDialect específico del proveedor.
Después de haber agregado jpaVendorAdapter a mi configuración de entityManagerFactory, todo funciona, tanto la consulta de JdbcTemplate como MyBatis se ejecutan en la misma transacción que se esperaba. Basado en el JavaDoc supongo que un jpaDialect debería ser suficiente, pero son las 4 de la mañana aquí, así que no intentaré eso ahora :)
<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>
Intenta usar:
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
que opera directamente en el nivel JDBC. Todas las abstracciones de persistencia (JPA / iBatis y JdbcTemplate
) eventualmente usan JDBC, por lo que debe manejar las transacciones en el nivel común más alto.
En su caso, está utilizando JpaTransactionManager
que maneja las transacciones a través de la abstracción javax.persistence.EntityTransaction
. Obviamente iBatis no tiene conocimiento de la transacción JPA, por lo tanto, presumiblemente funciona fuera de ella.
No necesita ninguna clase de cargador / instrumentación mágica, debería funcionar.
No tengo MyBatis en la mezcla, pero como tewe sugirió simplemente agregar el jpaDialect a la transactionManager también hace el trabajo.
<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>