MyBatis与Spring/Spring Boot深度集成指南

一、与Spring集成

1. SqlSessionFactoryBean

SqlSessionFactoryBean是Spring和MyBatis集成的核心类,负责创建SqlSessionFactory

@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws IOException {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
    factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
        .getResources("classpath:mappers/*.xml"));
    return factoryBean;
}

关键配置项:

  • dataSource: 必须注入的数据源
  • configLocation: 指定MyBatis全局配置文件
  • mapperLocations: 指定Mapper XML文件位置
  • typeAliasesPackage: 实体类所在包,自动注册别名

实践建议:

  1. 生产环境建议显式指定configLocation,即使大部分配置可通过Spring完成
  2. 使用mapperLocations通配符匹配时注意路径规范

2. MapperScannerConfigurer

自动扫描Mapper接口并注册为Spring Bean:

@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
    MapperScannerConfigurer configurer = new MapperScannerConfigurer();
    configurer.setBasePackage("com.example.mapper");
    configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
    return configurer;
}

配置说明:

  • basePackage: Mapper接口所在的包路径
  • annotationClass: 可指定自定义注解过滤接口
  • sqlSessionFactoryBeanName: 指定SqlSessionFactory的Bean名称

最佳实践:

  1. 将Mapper接口单独放在mapperdao包下
  2. 避免在Service层直接注入SqlSession

3. 事务管理

Spring的事务管理可与MyBatis无缝集成:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

事务配置要点:

  1. 使用@Transactional注解管理事务
  2. 默认情况下,每个Mapper方法都有自己的SqlSession
  3. 同一事务中的多个操作共享同一个SqlSession

常见问题解决:

// 解决事务内一级缓存失效问题
@Transactional
public void batchUpdate(List<User> users) {
    // 手动控制SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    try {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        users.forEach(mapper::update);
        sqlSession.commit();
    } finally {
        sqlSession.close();
    }
}

二、与Spring Boot集成

1. 自动配置原理

Spring Boot通过mybatis-spring-boot-starter提供自动配置:

graph TD
    A[MybatisAutoConfiguration] --> B[SqlSessionFactory]
    A --> C[SqlSessionTemplate]
    B --> D[DataSource]
    C --> B
    E[MapperScannerRegistrar] --> F[扫描@Mapper接口]

关键自动配置类:

  • MybatisAutoConfiguration: 核心配置类
  • MybatisProperties: 封装所有配置属性
  • MapperScannerRegistrar: 处理@MapperScan

2. 多数据源配置

@Configuration
public class MultiDataSourceConfig {
    
    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory(
            @Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("classpath:mapper/primary/*.xml"));
        return bean.getObject();
    }
    
    // 类似配置secondarySqlSessionFactory
}

多数据源事务管理:

@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(
        @Qualifier("primaryDataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

使用建议:

  1. 明确标记@Primary数据源
  2. 不同数据源的Mapper接口和XML文件分开存放
  3. 使用@Transactional时指定事务管理器

三、代码生成器

1. MyBatis Generator配置

典型generatorConfig.xml配置:

<context id="mysql" targetRuntime="MyBatis3">
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/test"
        userId="root" password="123456"/>
    
    <javaModelGenerator targetPackage="com.example.model"
        targetProject="src/main/java"/>
    
    <sqlMapGenerator targetPackage="mapper"
        targetProject="src/main/resources"/>
    
    <javaClientGenerator type="XMLMAPPER"
        targetPackage="com.example.mapper"
        targetProject="src/main/java"/>
    
    <table tableName="user" domainObjectName="User"
        enableCountByExample="false" enableUpdateByExample="false"/>
</context>

生成器类型选择:

  • ANNOTATEDMAPPER: 生成注解版Mapper
  • XMLMAPPER: 生成XML配置版
  • MIXEDMAPPER: 混合模式

2. 自定义插件示例

public class CustomPlugin extends PluginAdapter {
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, 
            IntrospectedTable introspectedTable) {
        // 添加Lombok注解
        topLevelClass.addAnnotation("@Data");
        topLevelClass.addAnnotation("@Builder");
        topLevelClass.addAnnotation("@NoArgsConstructor");
        topLevelClass.addAnnotation("@AllArgsConstructor");
        return true;
    }
}

生成器最佳实践:

  1. 将生成代码与手写代码分开管理
  2. 使用.gitignore忽略生成代码
  3. 自定义模板覆盖默认生成策略

四、TypeHandler深度应用

1. 自定义TypeHandler示例

处理JSON类型字段:

@MappedTypes({Map.class, List.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JsonTypeHandler extends BaseTypeHandler<Object> {
    
    private static final ObjectMapper mapper = new ObjectMapper();
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, 
            Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, mapper.writeValueAsString(parameter));
    }
    
    @Override
    public Object getNullableResult(ResultSet rs, String columnName) 
            throws SQLException {
        return parseJson(rs.getString(columnName));
    }
    
    private Object parseJson(String json) {
        try {
            return mapper.readValue(json, Object.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

2. 注册TypeHandler的三种方式

  1. XML配置

    <typeHandlers>
     <typeHandler handler="com.example.handler.JsonTypeHandler"/>
    </typeHandlers>
  2. Spring Boot配置

    mybatis.type-handlers-package=com.example.handler
  3. 注解方式

    @TableName(value = "user", autoResultMap = true)
    public class User {
     @TableField(typeHandler = JsonTypeHandler.class)
     private Map<String, Object> attributes;
    }

类型处理最佳实践:

  1. 复杂类型优先考虑JSON序列化方案
  2. 枚举类型实现EnumTypeHandler接口
  3. 敏感数据可在TypeHandler中加解密

五、集成常见问题解决方案

1. 事务不生效排查

检查清单:

  1. 确认@EnableTransactionManagement已启用
  2. 检查方法是否为public
  3. 确认异常类型是否被捕获未抛出
  4. 不同数据源是否使用了正确的事务管理器

2. 多数据源动态切换

基于AOP的动态数据源路由:

@Aspect
@Component
@Order(-1) // 确保在事务注解前执行
public class DataSourceAspect {
    
    @Before("@annotation(ds)")
    public void before(DataSourceSwitch ds) {
        String dsId = ds.value();
        if (!DynamicDataSource.contains(dsId)) {
            throw new IllegalArgumentException("数据源不存在");
        }
        DynamicDataSource.setDataSource(dsId);
    }
    
    @After("@annotation(ds)")
    public void after(DataSourceSwitch ds) {
        DynamicDataSource.clear();
    }
}

3. 性能优化建议

  1. 批处理优化

    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    
    public void batchInsert(List<User> users) {
     try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
         UserMapper mapper = session.getMapper(UserMapper.class);
         users.forEach(mapper::insert);
         session.commit();
     }
    }
  2. 二级缓存配置

    <cache 
     eviction="LRU"
     flushInterval="60000"
     size="512"
     readOnly="true"/>
  3. 延迟加载配置

    mybatis.configuration.lazy-loading-enabled=true
    mybatis.configuration.aggressive-lazy-loading=false

通过以上深度集成方案,可以充分发挥MyBatis在Spring生态中的优势,构建高效稳定的数据访问层。

添加新评论