首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring Data JPA 與 MyBatis 混合整合實戰:配置衝突、事務統一與最佳實踐

     編輯:Java 相關知識

業務背景

項目初期純 Spring Data JPA,後期引入 MyBatis 處理複雜多表關聯查詢。兩套 ORM 共存需解決:數據源共享、事務管理統一、Repository/Mapper 掃描隔離、懶加載異常。

核心配置類 (Spring Boot 2.x / 3.x 通用)

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.example.mapper", sqlSessionFactoryRef = "sqlSessionFactory")
@EnableJpaRepositories(basePackages = "com.example.repository", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
public class DualOrmConfig {

    @Primary // JPA 預設使用
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    // ========== MyBatis 配置 ==========
    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource ds) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(ds);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));
        bean.setTypeAliasesPackage("com.example.entity");
        // 關鍵:開啟駝峰映射、懶加載
        org.apache.ibatis.session.Configuration cfg = new org.apache.ibatis.session.Configuration();
        cfg.setMapUnderscoreToCamelCase(true);
        cfg.setLazyLoadingEnabled(true);
        cfg.setAggressiveLazyLoading(false);
        bean.setConfiguration(cfg);
        return bean.getObject();
    }

    @Bean
    public PlatformTransactionManager transactionManager(@Qualifier("dataSource") DataSource ds) {
        return new DataSourceTransactionManager(ds); // 統一事務管理器
    }

    // ========== JPA 配置 ==========
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSource") DataSource ds) {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(ds);
        em.setPackagesToScan("com.example.entity");
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        
        Properties props = new Properties();
        props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        props.setProperty("hibernate.hbm2ddl.auto", "update");
        props.setProperty("hibernate.show_sql", "false");
        props.setProperty("hibernate.format_sql", "true");
        // 關鍵:解決 no Session 懶加載異常
        props.setProperty("hibernate.enable_lazy_load_no_trans", "true");
        em.setJpaProperties(props);
        return em;
    }

    @Bean
    public JpaTransactionManager jpaTransactionManager(@Qualifier("entityManagerFactory") EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
}

application.yml 關鍵配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000
      validation-query: SELECT 1
      test-while-idle: true
  jpa:
    open-in-view: false # 關鍵:關閉 OSIV,避免懶加載隱患,Controller 層不再有 Session
  # mybatis 配置已在 Java Config 完成,這裡可留空

事務管理統一策略

場景建議
單一 JPA Repository 操作@Transactional 在 Service 方法上,自動使用 JpaTransactionManager
單一 MyBatis Mapper 操作同樣 @Transactional,DataSourceTransactionManager 兼容
混合操作 (JPA + MyBatis)同一 @Transactional 方法內調用,共用同一 DB 連接與事務,自動回滾
跨數據源需引入 JTA (Atomikos/Bitronix) 或 Seata,超出本文範圍

核心原則:同一 DataSource + 同一 PlatformTransactionManager + @Transactional,Spring 會自動綁定連接到 ThreadLocal,實現混合操作事務一致性。

常見坑點與解決方案

坑點現象解決
JPA 懶加載報錯org.hibernate.LazyInitializationException: could not initialize proxy - no Session1) 設置 hibernate.enable_lazy_load_no_trans=true 2) 推薦:Service 層用 @Transactional 或 DTO 投影查詢避免懶加載
MyBatis 駝峰映射失效數據庫字段 user_name 映射不到 userNameConfiguration 設置 mapUnderscoreToCamelCase=true
事務不生效混合操作拋異常但數據已提交確保 Service 類非 final、方法 public、@Transactional 在 public 方法上、掃描包正確
實體類衝突JPA Entity 與 MyBatis ResultType 共用類,註解衝突建議分包:entity (JPA) / dto (MyBatis ResultMap) 或統一用 JPA 註解兼容
雙主鍵生成策略衝突JPA @GeneratedValue vs MyBatis <selectKey>統一由數據庫自增/序列生成,JPA 用 GenerationType.IDENTITY,MyBatis useGeneratedKeys="true" keyProperty="id"

最佳實踐總結

  1. 分包隔離:repository (JPA) / mapper (MyBatis) / entity (共用或分離)
  2. 事務統一:單一 DataSourceTransactionManager,@Transactional 統管
  3. 查詢分工:簡單 CRUD、單表、分頁 → JPA;複雜多表、動態 SQL、報表 → MyBatis
  4. 關閉 OSIV:spring.jpa.open-in-view=false,強制 Service 層負責事務邊界
  5. 避免混用實體:JPA 實體帶有代理對象,傳給 MyBatis 可能序列化異常,建議 MyBatis 返回 DTO/VO

總結

Spring Data JPA + MyBatis 混合整合的核心在於 共享 DataSource、統一 TransactionManager、隔離掃描包路徑、關閉 OpenSessionInView。遵循上述配置模板與分工原則,即可在同一項目中享受 JPA 開發效率與 MyBatis SQL 掌控力的雙重紅利。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved