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

Posted

技术标签:

【中文标题】如何配置 Spring 使 JPA(Hibernate)和 JDBC(JdbcTemplate 或 MyBatis)共享同一个事务【英文标题】:How to configure Spring to make JPA (Hibernate) and JDBC (JdbcTemplate or MyBatis) share the same transaction 【发布时间】:2011-10-10 06:47:25 【问题描述】:

我有一个数据源,我使用 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 总是提供相同的连接,这没关系。

经过一番谷歌搜索后,我尝试了 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>

部分persistence.xml

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

【问题讨论】:

这能回答你的问题吗? What transaction manager should I use for JBDC template When using JPA ? 【参考方案1】:

我在这里找到了解决方案:What transaction manager should I use for JBDC template When using JPA ?

我使用的是 JpaTransactionManager 而不是 DataSourceTransactionManager。 JavaDochttp://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>

【讨论】:

是的,只需设置 jpa 方言即可emf.setJpaDialect(new HibernateJpaDialect());【参考方案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,因此您需要在最高通用级别上处理事务。

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

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

【讨论】:

"请注意,这需要配置供应商特定的 JpaDialect。" tewe 解决了问题-

以上是关于如何配置 Spring 使 JPA(Hibernate)和 JDBC(JdbcTemplate 或 MyBatis)共享同一个事务的主要内容,如果未能解决你的问题,请参考以下文章

手把手教你 Spring Boot 整合 Spring Data Jpa

我的spring和hibernate配置的一直报错,空指针

Spring 配置jpa(转)

如何使每个表中的主键值应从数字一(1)开始 - PostgreSQL,Spring data jpa

Spring集成JPA配置懒加载两个报错解决办法

Spring Boot / JPA / Hibernate,如何根据 Spring 配置文件切换数据库供应商?