1. mybatis-spring-boot-starter 引入依赖:
mybatis-spring-boot-autoconfigure
org.springframework.boot.autoconfigure.AutoConfiguration.imports 里:
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
2.
package org.mybatis.spring.boot.autoconfigure;
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
if (properties.getConfiguration() == null || properties.getConfiguration().getVfsImpl() == null) {
factory.setVfs(SpringBootVFS.class);
}
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
}
Resource[] mapperLocations = this.properties.resolveMapperLocations();
if (!ObjectUtils.isEmpty(mapperLocations)) {
factory.setMapperLocations(mapperLocations);
}
Set<String> factoryPropertyNames = Stream
.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
// Need to mybatis-spring 2.0.2+
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver = this.languageDrivers[0].getClass();
}
}
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
// Need to mybatis-spring 2.0.2+
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
}
applySqlSessionFactoryBeanCustomizers(factory);
return factory.getObject();
}
...
}
3.
使用 @MapperScan 注解或 MapperScannerConfigurer 配置类,指定 Mapper 接口所在的包路径。
Spring Boot 会扫描指定包下的所有接口,并将它们注册为 Spring Bean。
Spring Boot 自动配置了一个 SqlSessionFactory,它基于数据源(DataSource)和 MyBatis 的全局配置文件(如 mybatis-config.xml 或者代码中的配置)。
SqlSessionFactory 负责加载所有的 Mapper XML 文件,并将它们与对应的 Mapper 接口绑定。
当 Spring 容器初始化时,MyBatis 会为每个 Mapper 接口生成一个动态代理对象。
这个代理对象实现了 Mapper 接口的所有方法,并将方法调用转换为 SQL 执行操作。
(1)调用 Mapper 方法
开发者通过注入的 Mapper 接口调用某个方法,例如 userMapper.selectById(1)。
(2)动态代理拦截调用
MyBatis 的动态代理拦截了该方法调用,并解析方法签名(包括方法名、参数类型和返回值类型)。
(3)匹配 XML 中的 SQL
根据方法签名和 Mapper 接口的全限定名,MyBatis 查找对应的 XML 文件中的 <select> 标签(或其他标签),找到匹配的 SQL 语句。
(4)执行 SQL
MyBatis 使用 SqlSession 执行 SQL 操作,并将参数传递给 SQL。
结果集会被映射为返回值对象(如实体类或集合)。
SqlSession 的创建时机:
1. 手动创建 SqlSession
如果直接使用 MyBatis 而不依赖 Spring 等框架,开发者需要手动创建 SqlSession。通常通过 SqlSessionFactory 来获取 SqlSession 实例。
// 获取 SqlSessionFactory(通常在应用启动时初始化)
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 手动创建 SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 使用 SqlSession 执行操作
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.selectById(1);
session.commit(); // 提交事务
} catch (Exception e) {
session.rollback(); // 回滚事务
}
SqlSession
在调用sqlSessionFactory.openSession()
时被创建。- 每次调用
openSession()
方法都会生成一个新的SqlSession
实例。
2. Spring Boot 中的 SqlSession 创建
在 Spring Boot 中,MyBatis 的 SqlSession 创建由 Spring 容器管理,开发者通常不需要手动创建。以下是具体的工作机制:
(1)基于声明式事务管理
当使用 @Transactional
注解时,Spring 的事务管理器(如 DataSourceTransactionManager
)会负责创建和管理 SqlSession
。
- 事务开始时:Spring 会在事务开始时创建一个
SqlSession
,并将它绑定到当前线程。 - 事务结束时:在事务提交或回滚后,Spring 会关闭
SqlSession
并释放底层的数据库连接。
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void addUser(User user) {
userMapper.insertUser(user); // 自动使用 Spring 管理的 SqlSession
}
}
SqlSession
在事务开始时由 Spring 创建。- 如果当前线程已经存在一个
SqlSession
(例如在同一个事务中),Spring 会复用这个SqlSession
,而不会创建新的实例。
(2)非事务环境下的 SqlSession
即使没有声明事务,Spring 仍然会为每次 Mapper 方法调用创建一个 SqlSession
。
- 创建时机:当调用 Mapper 接口的方法时,Spring 会检查当前线程是否已有
SqlSession
。如果没有,则会创建一个新的SqlSession
。 - 关闭时机:方法执行完成后,Spring 会自动关闭
SqlSession
。
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(int id) {
return userMapper.selectById(id); // 自动创建和关闭 SqlSession
}
}