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

MyBatis 與 Spring 整合完整指南:從基礎配置到事務管理最佳實踐

     編輯:Java 相關知識

整合必要性

  • 單獨 MyBatis 無法實現跨 SqlSession 事務
  • 企業級應用多由 Spring 統一管理事務、數據源、Bean 生命週期
  • 整合後:DAO 只需定義接口,實現類由 MyBatis-Spring 自動生成代理

版本兼容性基線

組件最低版本建議版本 (2024)
MyBatis3.0+3.5.x
Spring3.0+5.3.x / 6.1.x
MyBatis-Spring1.0+3.0.x

核心配置詳解

1. 數據源與事務管理器

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

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

<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>

2. SqlSessionFactoryBean —— 核心工廠

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
    <property name="typeAliasesPackage" value="com.example.dto"/>
    <property name="mapperLocations" value="classpath*:mapper/**/*.xml"/>
</bean>
  • configLocation:MyBatis 原生配置 (緩存、設定等)
  • typeAliasesPackage:自動掃描別名,省去 XML <typeAlias>
  • mapperLocations:批量掃描映射文件,替代手動 <mapper>

3. Mapper 掃描器 —— 消除重複 Bean 定義

<!-- 單模板掃描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.dao"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

<!-- 多模板區分 (SIMPLE vs BATCH) -->
<bean id="sqlSessionTemplateSimple" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="sqlSessionFactory"/>
    <constructor-arg value="SIMPLE"/>
</bean>
<bean id="sqlSessionTemplateBatch" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="sqlSessionFactory"/>
    <constructor-arg value="BATCH"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.dao"/>
    <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplateSimple"/>
    <property name="markerInterface" value="com.example.dao.SimpleDao"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.dao"/>
    <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplateBatch"/>
    <property name="annotationClass" value="com.example.dao.BatchAnnotation"/>
</bean>

關鍵屬性:

  • markerInterface:實現該接口的 DAO 才被掃描 (接口標記)
  • annotationClass:標註該註解的 DAO 被掃描 (註解標記)
  • 二者互斥,只能二選一

註解驅動配置 (Spring Boot / Java Config 推薦)

@Configuration
@MapperScan(basePackages = "com.example.dao", 
            sqlSessionTemplateRef = "sqlSessionTemplate")
public class MyBatisConfig {
    
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("classpath*:mapper/**/*.xml"));
        bean.setTypeAliasesPackage("com.example.dto");
        return bean.getObject();
    }
    
    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory factory) {
        return new SqlSessionTemplate(factory, ExecutorType.SIMPLE);
    }
    
    @Bean
    public DataSourceTransactionManager txManager(DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
}

完整實戰範例:Emp 實體 CRUD + 事務測試

實體與 Mapper

// Emp.java
public class Emp {
    private String id, name, sex, phone;
    private int age;
    // getter/setter
}

// EmpMapper.java
public interface EmpMapper {
    void insertEmp(Emp emp);
    List getAllEmp();
    Emp getById(String id);
    void deleteEmp(String id);
    void updateEmp(Map map);
}

// EmpMapper.xml
<mapper namespace="com.example.mapper.EmpMapper">
    <resultMap id="empMap" type="Emp">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="sex" column="sex"/>
        <result property="age" column="age"/>
        <result property="phone" column="phone"/>
    </resultMap>
    <insert id="insertEmp" parameterType="Emp">
        INSERT INTO emp(id,name,sex,age,phone) VALUES(#{id},#{name},#{sex},#{age},#{phone})
    </insert>
    <select id="getAllEmp" resultMap="empMap">SELECT * FROM emp</select>
    <select id="getById" parameterType="String" resultMap="empMap">
        SELECT * FROM emp WHERE id=#{id}
    </select>
    <delete id="deleteEmp" parameterType="String">DELETE FROM emp WHERE id=#{id}</delete>
    <update id="updateEmp" parameterType="Map">
        UPDATE emp SET name=#{name},sex=#{sex},age=#{age},phone=#{phone} WHERE id=#{id}
    </update>
</mapper>

Service 層與事務控制

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired EmpMapper empMapper; // 直接注入 Mapper 接口
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertEmp(Emp emp) {
        empMapper.insertEmp(emp);
        // 模擬異常驗證回滾
        if ("rollback".equals(emp.getName())) throw new RuntimeException("測試回滾");
    }
}

AOP XML 事務配置 (傳統項目)

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="servicePc" expression="execution(* com.example.service..*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePc"/>
</aop:config>

事務管理三種方式對比

方式適用場景優點缺點
@Transactional 註解現代項目、Spring Boot簡潔、類/方法級靈活控制需開啟代理、自我調用失效
AOP XML (tx:advice)傳統 XML 配置項目集中管理、無侵入代碼配置冗長、重構不便
TransactionTemplate 編程式動態事務邏輯、非 Spring 管理 Bean精確控制邊界、無代理限制代碼侵入、樣板代碼多

常見坑點避雷

  • 數據源不一致:txManager 與 sqlSessionFactory 必須引用同一個 dataSource,否則事務不生效。
  • 自我調用失效:同類內部方法調用 @Transactional 方法不生效 (繞過代理),解決:注入自身 @Autowired SelfService self 或拆分類。
  • 異常吞噬:catch (Exception e) 未重拋導致事務提交,必須 throw e 或標註 rollbackFor。
  • MapperScannerConfigurer 重複掃描:多實例需互斥 markerInterface/annotationClass,否則 Bean 衝突。
  • 批量執行器未提交:ExecutorType.BATCH 需手動 sqlSession.commit() 或配置 Spring 事務管理,否則數據不入庫。

總結

MyBatis-Spring 整合核心在於:SqlSessionFactoryBean 解析配置、MapperFactoryBean 生成 Mapper 代理、MapperScannerConfigurer 批量註冊、SqlSessionTemplate 適配 Spring 事務。掌握「單模板/多模板掃描」、「註解/XML 事務三選一」、「數據源一致性」三大核心點,即可構建穩健、可維護的持久層架構。新項目強烈建議採用 Spring Boot + @MapperScan + @Transactional 註解驅動模式。

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