Jak skonfigurować Spring, aby JPA (Hibernate) i JDBC (JdbcTemplate lub MyBatis) współdzieliły tę samą transakcję

Mam jedno źródło danych, używam Spring 3.0.3, Hibernate 3.5.1 jako dostawca JPA i używam MyBatis 3.0.2 Dla niektórych zapytań i moja aplikacja działa na Tomcat 6. Mam HibernateDAO i MyBatisDAO, kiedy wywołuję oba z tej samej metody, która jest przypisana do @ Transactional wygląda na to, że nie dzielą tej samej transakcji, dostają różne połączenia.
Jak mogę je zrobić?

Próbowałem nawiązać połączenie z DataSourceUtils.getConnection (dataSource) i dostaję taki, który jest używany przez MyBatis co jest dziwne myślałem, że problem był w mybatis config i nie może używać JpaTransactionManager. Nawet wielokrotne wywołanie DataSoruceUtils.getConnection daje zawsze to samo połączenie, co jest w porządku.

Po jakimś googlowaniu wypróbowałem Spring-instrument-Tomcat ' s classloader (chociaż Nie wiem, czy tomcat naprawdę go używa:))

Częściowy 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>

Częściowa konfiguracja mybatis

<settings>
    <setting name="cacheEnabled" value="false" />
    <setting name="useGeneratedKeys" value="false" />
    <setting name="defaultExecutorType" value="REUSE" />
    <setting name="lazyLoadingEnabled" value="false"/>
</settings>

Częściowy wytrwałość.xml

<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
Author: tewe, 2011-07-21

3 answers

Znalazłem rozwiązanie tutaj: Jakiego menedżera transakcji powinienem użyć do szablonu JBDC podczas korzystania z JPA ?

Używam JpaTransactionManager, a nie DataSourceTransactionManager.
JavaDoc http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html

Ten Menedżer transakcji obsługuje również bezpośredni dostęp do źródeł danych w ramach transakcji (tj. zwykły kod JDBC pracujący z tym samym DataSource) . Pozwala to na mieszanie usług, które mają dostęp do JPA i usług, które używają zwykłego JDBC (bez świadomości JPA)! Kod aplikacji musi trzymać się tego samego prostego wzorca wyszukiwania połączeń, co w przypadku DataSourceTransactionManager (tzn. DataSourceUtils.getConnection (javax.sql.DataSource) lub przechodząc przez transakcję). Należy pamiętać, że wymaga to skonfigurowania JpaDialect specyficznego dla dostawcy.

Po dodaniu jpaVendorAdapter do mojego config entityManagerFactory wszystko działa, zarówno zapytanie JdbcTemplate, jak i MyBatis działają w tej samej transakcji zgodnie z oczekiwaniami. Bazując na JavaDoc myślę, że jpadialect powinien wystarczyć, ale jest 4 rano, więc nie będę tego teraz próbował:)

<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>
 34
Author: tewe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:24:56

Nie mam Mybatisa w miksie, ale jak zasugerował tewe samo dodanie jpaDialect do transactionmanagera też to robi.

<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>
 4
Author: sbk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-05 15:51:43

Spróbuj użyć:

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

Który działa bezpośrednio na poziomie JDBC. Wszystkie abstrakcje persistence (JPA / iBatis i JdbcTemplate) ostatecznie używają JDBC, więc musisz obsługiwać transakcje na najwyższym wspólnym poziomie.

W Twoim przypadku używasz JpaTransactionManager, który obsługuje transakcje za pomocą javax.persistence.EntityTransaction abstrakcji. Oczywiście iBatis nie jest świadomy transakcji JPA, stąd prawdopodobnie działa poza nią.

Nie potrzebujesz żadnej magii klasy / oprzyrządowania, powinno po prostu działać.

 -2
Author: Tomasz Nurkiewicz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-21 14:20:08