Как настроить Spring так, чтобы JPA (Hibernate) и JDBC (JdbcTemplate или MyBatis) совместно использовали одну и ту же транзакцию - PullRequest
24 голосов
/ 21 июля 2011

У меня есть один источник данных, я использую Spring 3.0.3, Hibernate 3.5.1 в качестве поставщика JPA, и я использую MyBatis 3.0.2 для некоторых запросов, и мое приложение работает на Tomcat 6. У меня есть HibernateDAO и MyBatisDAO, когда Я вызываю оба из одного и того же метода, который аннотирован @Transactional, похоже, что они не разделяют одну и ту же транзакцию, они получают разные соединения.
Как я могу заставить их делать?

Я попытался получить соединение от DataSourceUtils.getConnection (dataSource), и я получил тот, который используется MyBatis, что странно, я думал, что проблема была в конфигурации MyBatis, и он не может использовать JpaTransactionManager. Даже многократный вызов DataSoruceUtils.getConnection всегда дает одно и то же соединение, и это нормально.

После некоторого поиска в Google я попробовал загрузчик классов Spring-Instrument-Tomcat (хотя я не знаю, действительно ли tomcat использует его:))

частичное применениеКонтекст

<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>

частичное сохранение.xml

<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>

Ответы [ 3 ]

35 голосов
/ 22 июля 2011

Я нашел решение здесь: Какой менеджер транзакций я должен использовать для шаблона JBDC При использовании JPA?

Я использую JpaTransactionManager, а не DataSourceTransactionManager.
JavaDoc http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html

Этот менеджер транзакций также поддерживает прямой доступ к источникам данных внутри транзакции (т. Е. Обычный код JDBC, работающий с тем же источником данных) .Это позволяет смешивать сервисы, которые получают доступ к 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>
4 голосов
/ 05 декабря 2011

У меня нет MyBatis в миксе, но, как мы и предложили, просто добавив jpaDialect в TransactionsManager, тоже сделает свою работу.

<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>
0 голосов
/ 21 июля 2011

Попробуйте использовать:

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

, который работает непосредственно на уровне JDBC. Все абстракции персистентности (JPA / iBatis и JdbcTemplate) в конечном итоге используют JDBC, поэтому вам нужно обрабатывать транзакции на самом высоком общем уровне.

В вашем случае вы используете JpaTransactionManager, который обрабатывает транзакции через javax.persistence.EntityTransaction абстракцию. Очевидно, что iBatis не знает о транзакции JPA, следовательно, предположительно она работает вне ее.

Вам не нужно никакого волшебства загрузчика классов / инструментовки, он должен просто работать.

...