广告位 · 页头(后台-模块管理-新建模块后替换此区域)

如何把MyBatis整合到Spring中?详细步骤、使用体验与常见问题解析

MyBatis与Spring的整合并不是简单地把两个框架堆在一起,而是要解决两个核心问题:谁来创建SqlSessionFactory,以及如何让Mapper接口能被Spring自动注入。原生MyBatis中通常会手动调用Resources.getResourceAsReader,而在Spring容器中这一职责被SqlSessionFactoryBean和MapperScannerConfigurer接替。整体配置分为依赖引入、DataSource定义、SqlSessionFactoryBean构建、事务管理以及Mapper扫描五步。

如何把MyBatis整合到Spring中?详细步骤、使用体验与常见问题解析

一、整合前的基础准备与依赖引入

在开始配置之前,先要确保项目使用Maven或Gradle管理依赖。以Maven项目为例,源码目录通常放在C:\workspace\mybatis-spring-demo,配置文件放在src\main\resources下。这里需要引入四个核心依赖:mybatis、mybatis-spring、spring-context和spring-jdbc,数据库驱动根据实际情况选择MySQL或Oracle等。

版本兼容性是很多开发者容易忽略的地方。MyBatis 3.5.x系列建议搭配mybatis-spring 2.x版本,Spring Framework 5.3.x与Java 8及以上版本兼容性最好。如果使用Spring Framework 6.x,则需要升级到MyBatis 3.5.9以上,并且JDK版本不低于17。Maven本地仓库中这些jar包会下载到C:\Users\Administrator\.m2\repository目录下。

下面给出一个精简的pom.xml依赖片段,实际项目中还需要添加spring-context-support、spring-tx等传递依赖,但核心是这一部分。

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.29</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.29</version>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

引入这些依赖后,就可以开始编写Spring的XML配置文件。如果项目使用纯注解方式,也可以直接通过@Configuration类完成同样的工作,但XML配置在传统Spring MVC项目中仍然很常见,而且可读性更强,适合作为理解整合原理的起点。

二、核心XML配置:数据源、SqlSessionFactory与事务

Spring整合MyBatis的关键在于把MyBatis的SqlSessionFactory声明为一个Spring Bean。这一步通过org.mybatis.spring.SqlSessionFactoryBean实现,它是一个工厂Bean,内部会读取MyBatis全局配置文件,并注入数据源。数据源本身可以用Spring自带的DriverManagerDataSource,也可以换成Druid、HikariCP等连接池。

下面是一个完整的applicationContext.xml配置示例。注意sqlSessionFactory这个Bean包含三个属性:dataSource指定数据源,configLocation指定MyBatis全局配置文件的位置,mapperLocations指定Mapper XML映射文件的扫描路径。路径使用classpath:前缀,这里扫描classpath:mapper/*.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.example.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>
</beans>

上述配置中tx:annotation-driven用于开启基于注解的事务管理,MapperScannerConfigurer会在指定的basePackage下查找所有接口,并自动将它们注册为Spring Bean,省去了为每个Mapper单独配置MapperFactoryBean的麻烦。同时,sqlSessionFactoryBeanName属性必须指定,否则当MapperScannerConfigurer在容器初始化早期创建时,可能无法正确注入sqlSessionFactory。

MyBatis全局配置文件mybatis-config.xml可以保持精简,大部分设置都可以直接写在Spring配置中。不过有些行为仍然需要通过该文件配置,比如驼峰映射、类型别名和二级缓存等。下面是一个示例。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
        <package name="com.example.domain"/>
    </typeAliases>
</configuration>

完成以上配置后,Spring容器启动时就会自动创建SqlSessionFactory和事务管理器,Mapper接口也会被扫描并注入到需要的地方。接下来看看如何编写Mapper接口和对应的Service层。

三、Mapper接口扫描与注解方式整合

在Spring中,最常用的Mapper注册方式有两种:一种是通过上面的XML配置使用MapperScannerConfigurer,另一种是直接使用@MapperScan注解。两者原理相同,都是扫描指定包下面的接口,并生成代理实现。对于习惯Java配置的项目,@MapperScan更加直观。

下面是一个纯注解风格的配置类,它完成与上一节XML相同的工作。注意@MapperScan注解写在配置类上,@EnableTransactionManagement开启事务。数据源和事务管理器通过@Bean方式定义。

import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("com.example.mapper")
@EnableTransactionManagement
public class MyBatisConfig {
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return factoryBean.getObject();
    }
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

Mapper接口本身不需要实现类,直接编写接口方法并使用@Select注解或XML文件映射即可。比如一个根据ID查询用户的Mapper可以写成这样。

package com.example.mapper;
import com.example.domain.User;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);
}

Service层通过构造器注入UserMapper,并在需要事务的方法上标注@Transactional。Spring会使用配置好的事务管理器来管理连接提交和回滚,不再需要手动调用SqlSession.commit()或rollback()。

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.mapper.UserMapper;
import com.example.domain.User;
@Service
public class UserService {
    private final UserMapper userMapper;
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
    @Transactional
    public User getUser(Long id) {
        return userMapper.findById(id);
    }
}

这种整合方式最大的好处是Mapper接口可以直接注入到Service中,不需要每次操作都打开SqlSession,也不用手动注册Mapper。Spring AOP会在事务边界内管理SqlSession的创建和关闭,开发体验非常接近Spring Data JPA。

四、使用体验、功能亮点与常见问题

整合完成后,实际使用体验明显比原生MyBatis顺畅。去掉了大量模板代码,Service层不再关心SqlSessionFactory的获取和SqlSession的关闭。同时,MyBatis的XML映射文件仍然可以保留复杂SQL,动态SQL标签如<if>、<foreach>在Spring环境中照常工作,没有因为容器管理而损失灵活性。

功能亮点方面,最实用的是驼峰自动映射。通过mapUnderscoreToCamelCase设置,数据库字段user_name可以自动映射到Java属性userName,不用在resultMap中逐个指定。类型别名可以简化XML中的参数类型和返回类型,Mapper扫描支持多个包,事务管理由Spring统一处理,二级缓存和懒加载等MyBatis特性也可以继续使用,只是缓存的作用域需要结合SqlSession生命周期来理解。

常见问题中,出现频率最高的是Invalid bound statement (not found)。这个异常通常表示Mapper接口与XML映射文件的namespace不匹配,或者mapperLocations路径写错。解决方法是检查XML文件中namespace是否完全等于接口的全限定名,以及XML文件是否放在classpath:mapper/目录下。另一个高频错误是Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required,这往往是因为MapperScannerConfigurer配置中忘记了设置sqlSessionFactoryBeanName,或者把它写成了ref而不是value。

事务不生效也是常见问题。如果Service方法上添加了@Transactional但数据库操作没有回滚,要检查tx:annotation-driven是否配置、方法是否是public、异常是否属于运行时异常。另外,如果项目中使用多数据源,建议把不同业务模块的Mapper拆分到不同包,并配置多个SqlSessionFactory,每个工厂指定不同的mapperLocations,再用不同的MapperScannerConfigurer扫描对应包,避免数据源串用导致的脏数据问题。

广告位 · 文章底部(后台-模块管理替换此区域)