Как настроить Spring, чтобы JPA (Hibernate) и JDBC (JdbcTemplate или MyBatis) использовали одну и ту же транзакцию
У меня есть один источник данных, я использую Spring 3.0.3, Hibernate 3.5.1 как поставщик JPA, и я использую MyBatis 3.0.2 для некоторых запросов, а мое приложение работает на Tomcat 6. У меня есть HibernateDAO и MyBatisDAO, когда я вызываю оба из того же метода, который аннотируется с @Transactional, похоже, что они не используют одну и ту же транзакцию, они получают разные соединения.
Как я могу заставить их делать?
Я попытался получить соединение с DataSourceUtils.getConnection(dataSource), и я получаю тот, который используется MyBatis, что странно. Я думал, что проблема была в MyBatis config и не может использовать JpaTransactionManager. Даже вызывая несколько раз DataSoruceUtils.getConnection, всегда поддерживает одно и то же соединение. Это нормально.
После некоторого googling я пробовал spring -instrument-tomcat classloader (хотя я не знаю, действительно ли tomcat использует его:))
частичное приложениеContext
<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>
частичная конфигурация mybatis
<settings>
<setting name="cacheEnabled" value="false" />
<setting name="useGeneratedKeys" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
partial persistence.xml
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
Ответы
Ответ 1
Я нашел решение здесь: Какой менеджер транзакций я должен использовать для шаблона JBDC При использовании JPA?
Я использую JpaTransactionManager, а не DataSourceTransactionManager.
JavaDoc http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html
Этот диспетчер транзакций также поддерживает прямой доступ к DataSource в транзакции (т.е. простой JDBC-код, работающий с тем же DataSource). Это позволяет смешивать службы, которые обращаются к JPA и службам, которые используют простой JDBC (не зная JPA)! Код приложения должен придерживаться того же простого шаблона поиска соединения, что и в DataSourceTransactionManager (т.е. DataSourceUtils.getConnection(javax.sql.DataSource) или через TransactionAwareDataSourceProxy). Обратите внимание, что для этого необходимо настроить JpaDialect для конкретного поставщика.
После добавления jpaVendorAdapter в мою конфигурацию entityManagerFactory все работает, и запрос JdbcTemplate, и MyBatis выполняется в той же транзакции, что и ожидалось. На основе JavaDoc я предполагаю, что jpaDialect должен быть достаточно, но это 4 часа, поэтому я не буду пытаться это сейчас:)
<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>
Ответ 2
У меня нет MyBatis в миксе, но, поскольку tewe предложил просто добавить jpaDialect к transactionManager, это тоже задание.
<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>
Ответ 3
Попробуйте использовать:
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
который работает непосредственно на уровне JDBC. Все абстракции настойчивости (JPA/iBatis и JdbcTemplate
) в конечном итоге используют JDBC, поэтому вам необходимо обрабатывать транзакции на самом высоком общем уровне.
В вашем случае вы используете JpaTransactionManager
, который обрабатывает транзакции через абстракцию javax.persistence.EntityTransaction
. Очевидно, iBatis не знает о транзакции JPA, поэтому, предположительно, она работает за ее пределами.
Вам не нужна волшебная игра-манипулятор класса/инструментария, он должен работать.