spring 如何配置Spring让JPA(Hibernate)和JDBC(JdbcTemplate或MyBatis)共享同一个事务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6777419/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 04:35:56  来源:igfitidea点击:

How to configure Spring to make JPA (Hibernate) and JDBC (JdbcTemplate or MyBatis) share the same transaction

hibernatespringjpatransactionsmybatis

提问by tewe

I have a single dataSource, I use Spring 3.0.3, Hibernate 3.5.1 as JPA provider and I use MyBatis 3.0.2 for some queries and my app runs on Tomcat 6. I have a HibernateDAO and a MyBatisDAO, when I call both from the same method which is annotated with @Transactional it looks like they don't share the same transaction, they get different connections.
How can I make them to do?

我有一个数据源,我使用 Spring 3.0.3,Hibernate 3.5.1 作为 JPA 提供者,我使用 MyBatis 3.0.2 进行一些查询,我的应用程序在 Tomcat 6 上运行。我有一个 HibernateDAO 和一个 MyBatisDAO,当我调用两者时从用@Transactional 注释的相同方法看来,它们似乎不共享相同的事务,它们获得了不同的连接。
我怎样才能让他们去做?

I've tried getting a connection from DataSourceUtils.getConnection(dataSource) and I get the one which is used by MyBatis which is strange I thought the problem was in MyBatis config and it can't use JpaTransactionManager. Even calling multiple times DataSoruceUtils.getConnection gives the same connection always, which is ok.

我试过从 DataSourceUtils.getConnection(dataSource) 获取连接,我得到了 MyBatis 使用的连接,这很奇怪,我认为问题出在 MyBatis 配置中,它不能使用 JpaTransactionManager。即使多次调用 DataSoruceUtils.getConnection 也会始终提供相同的连接,这没关系。

After some googling I've tried spring-instrument-tomcat's classloader (although I don't know if tomcat really uses it :))

经过一番谷歌搜索后,我尝试了 spring-instrument-tomcat 的类加载器(虽然我不知道 tomcat 是否真的使用它:))

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>

partial mybatis config

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

部分持久化.xml

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

回答by tewe

I've found the solution here: What transaction manager should I use for JBDC template When using JPA ?

我在这里找到了解决方案:使用 JPA 时,我应该为 JBDC 模板使用什么事务管理器?

I'm using JpaTransactionManager and not DataSourceTransactionManager.
JavaDoc http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html

我使用的是 JpaTransactionManager 而不是 DataSourceTransactionManager。
JavaDoc http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaTransactionManager.html

This transaction manager also supports direct DataSource access within a transaction (i.e. plain JDBC code working with the same DataSource). This allows for mixing services which access JPA and services which use plain JDBC (without being aware of JPA)! Application code needs to stick to the same simple Connection lookup pattern as with DataSourceTransactionManager (i.e. DataSourceUtils.getConnection(javax.sql.DataSource) or going through a TransactionAwareDataSourceProxy). Note that this requires a vendor-specific JpaDialect to be configured.

该事务管理器还支持事务内的直接数据源访问(即使用相同数据源的普通 JDBC 代码)。这允许混合访问 JPA 的服务和使用普通 JDBC 的服务(不知道 JPA)!应用程序代码需要坚持与 DataSourceTransactionManager 相同的简单连接查找模式(即 DataSourceUtils.getConnection(javax.sql.DataSource) 或通过 TransactionAwareDataSourceProxy)。请注意,这需要配置特定于供应商的 JpaDialect。

After I've added jpaVendorAdapter to my entityManagerFactory config everything works, both JdbcTemplate query and MyBatis runs in the same transaction as expected. Based on the JavaDoc I guess a jpaDialect should be enough but it's 4 a.m. here so I won't try that now :)

在我将 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>

回答by sbk

I haven't got MyBatis in the mix, but as tewe suggested just adding the jpaDialect to the transactionManager does the job too.

我没有混合使用 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>

回答by Tomasz Nurkiewicz

Try using:

尝试使用:

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

which operates directly on JDBC level. All persistence abstractions (JPA/iBatis and JdbcTemplate) are eventually using JDBC, so you need to handle the transactions on the highest common level.

它直接在 JDBC 级别上运行。所有持久化抽象(JPA/iBatis 和JdbcTemplate)最终都使用 JDBC,因此您需要在最高公共级别上处理事务。

In your case you are using JpaTransactionManagerthat handles transactions via javax.persistence.EntityTransactionabstraction. Obviously iBatis is not aware of JPA transaction, hence presumably it works outside of it.

在您的情况下,您使用的JpaTransactionManager是通过javax.persistence.EntityTransaction抽象处理事务。显然 iBatis 不知道 JPA 事务,因此大概它在它之外工作。

You don't need any class loader/instrumentation magic, should just work.

你不需要任何类加载器/检测魔法,应该可以工作。