前言
目前,关系型数据库已经成为 Java 应用的标配,由于 JDBC 操作数据库的复杂性,我们通常会选择一款持久层框架,而作为半自动化 ORM 框架的 MyBatis 则成了我们的首选,而 Spring 又成了 Java 事实上的标准,它让我们开发 Java 应用更快、更容易、更安全,因此我们通常会将 MyBatis 与 Spring 整合在一起使用。
MyBatis 脱离 Spring 环境已经可以使用了,那为什么又要将它与 Spring 整合在一起呢?这里我总结出两点。
- 配置简化,和 Spring 整合在一起后复杂的 xml 配置可以忽略或少量配置。
- 加入 Spring 事务管理,将 MyBatis 异常转换为 Spring 中的异常。
快速上手
MyBatis 与 Spring 的整合可以分为两块。一块是直接与 Spring Framework 整合,整合后我们就可以在 Spring 的事务管理下使用 MyBatis,此时还需要少量的配置。另一块是与 SpringBoot 整合,整合后几乎可以忽略所有的 MyBatis 配置,极个别配置在 application.properties 添加即可。
这里假定屏幕前的小伙伴对 MyBatis 具备一定的认识,如果你认为还不够熟悉 MyBatis,可以参考我前面的几篇文章先进行了解。
- MyBatis 初探,使用 MyBatis 简化数据库操作(超详细)
- MyBatis Mapper 接口方法执行原理分析
- 一条 SQL 是如何在 MyBatis 中执行的
- 谈谈 MyBatis 的插件,除了分页你可能还有这些使用场景
- MyBatis 缓存机制分析,MyBatis 真的有二级缓存?
- MyBatis 中的设计模式,这次我总结全了
- MyBatis TypeHandler 泛型踩坑指南
Spring Framework 整合
在不考虑原理的情况下,我们先看如何在 Spring 环境中使用 MyBatis。
为了将 MyBatis 应用到 Spring,MyBatis 官方提出了一个子项目 mybatis-spring,需要先将其引入到项目中。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
SqlSessionFactory 配置及使用
SqlSession 是 Mybatis 执行 SQL 的入口,它又非线程安全,因此我们可以在 Spring 环境下配置一个线程安全的 SqlSessionFactory 作为 bean。按照非 Spring 环境下的方式创建 SqlSessionFactory 也并无不可,然而为了简化配置及加入 Spring 的事务管理,mybatis-spring 项目中提供了一个 SqlSessionFactoryBean,这是一个 Spring 的 FactoryBean,最终 Spring 容器创建的是一个从 SqlSessionFactoryBean 获取到的 SqlSessionFactory,因此我们配置 SqlSessionFactoryBean 作为 bean 即可。
@Configuration
public class MyBatisConfiguration {
@Bean
public DataSource dataSource() {
DataSource dataSource = new PooledDataSource();
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 设置数据源
sqlSessionFactoryBean.setDataSource(dataSource());
// 设置 mapper xml 文件路径
Resource[] mapperResources = new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*Mapper.xml");
sqlSessionFactoryBean.setMapperLocations(mapperResources);
// 设置配置
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// 字段映射时先将数据库表字段下划线转驼峰
configuration.setMapUnderscoreToCamelCase(true);
// 关闭缓存
configuration.setLocalCacheScope(LocalCacheScope.STATEMENT);
configuration.setCacheEnabled(false);
sqlSessionFactoryBean.setConfiguration(configuration);
return sqlSessionFactoryBean;
}
}
SqlSessionFactoryBean 只有一个必要的 dataSource 属性需要设置,除此之外,我们还配置了 mapper xml 配置文件的路径,以及常用的关闭缓存及下划线转驼峰的字段映射方式,此时我们已经可以使用如下的方式使用 MyBatis。
@Service
public class UserService {
@Autowired
private SqlSessionFactory sqlSessionFactory;
public User getById(Integer id) {
return sqlSessionFactory.openSession().selectOne("com.zzuhkp.blog.mybatis.mapper.UserMapper.getById", id);
}
}
SqlSessionTemplate 配置及使用
通过上面对 SqlSessionFactoryBean 的配置我们已经可以获取到 SqlSession 来执行 SQL,但是这个 SqlSession 不是线程安全的,因此 MyBatis 又提供了一个线程安全的 SqlSession,为 SqlSessionTempalte,将其配置为 Spring 的 bean 即可方便的使用。
@Configuration
public class MyBatisConfiguration {
@Bean
public DataSource dataSource() {
DataSource dataSource = new PooledDataSource();
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
... 省略部分代码
return sqlSessionFactoryBean;
}
@Bean
public SqlSession sqlSession() throws Exception {
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject());
return sqlSessionTemplate;
}
}
此时业务代码使用 MyBatis 方式如下。
@Service
public class UserService {
@Autowired
private SqlSession sqlSession;
public User getById(Integer id) {
return sqlSession.selectOne("com.zzuhkp.blog.mybatis.mapper.UserMapper.getById", id);
}
}
Mapper 配置及使用
通过上面的配置虽然我们已经有了线程安全的 SqlSessionTemplate,但是通常情况我们更希望使用 Mapper 接口操作数据库,Spring 环境下就需要把 Mapper 接口注入为 bean 了,通过 SqlSession#getMapper 方法获取是一个不错的选择,但是 mybatis 还提供了不通过 SqlSession 获取 Mapper 的 MapperFactoryBean 类,这个类可以创建 Mapper 接口的实现作为 bean。
@Configuration
public class MyBatisConfiguration {
@Bean
public DataSource dataSource() {
DataSource dataSource = new PooledDataSource();
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
...省略部分代码
return sqlSessionFactoryBean;
}
@Bean
public MapperFactoryBean<UserMapper> userMapper() throws Exception {
MapperFactoryBean<UserMapper> factoryBean = new MapperFactoryBean<>();
factoryBean.setMapperInterface(UserMapper.class);
factoryBean.setSqlSessionFactory(sqlSessionFactory().getObject());
return factoryBean;
}
}
通过上面的代码,我们就可以按照如下的方式使用 UserMapper 替代 SqlSession 了。
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getById(Integer id) {
return userMapper.getById(id);
}
}
Mapper 扫描配置
通过上面的方式我们直接将 Mapper 配置为 bean,如果项目中有几十甚至几百个 Mapper,那配置的工作量将大大增加,为了简化配置,MyBatis 提出了一个 @MpperScan 注解,这个注解会为扫描到的 Mapper 接口创建代理类,然后注入到 Spring 容器中,将这个注解添加到 Spring 配置类上即可。
@MapperScan("com.zzuhkp.blog.mybatis.mapper")
@Configuration
public class MyBatisConfiguration {
@Bean
public DataSource dataSource() {
DataSource dataSource = new PooledDataSource();
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
... 省略部分代码
return sqlSessionFactoryBean;
}
}
使用方式和上面注入 UserMapper 的使用方式相同,从这里也可以看出,MyBatis 在不断简化配置以及降低使用的难度。
SpringBoot 整合
引入 mybatis-spring 依赖后,再加上少量的配置已经可以在 Spring 中使用 MyBatis 了,但是不满足是向上的车轮,当前我们使用更多的是 SpringBoot,它大大简化了使用 Spring 做出的大量配置,为了在 SpringBoot 环境下进一步简化 MyBatis 的使用,MyBatis 官网又提出了一个 starter,坐标如下。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
是的,仅仅添加上面一个依赖即可,然后就可以直接注入 Mapper 执行 SQL 了。那它有何魔法呢?默认情况这个 starter 在背后会做如下的工作。
- 自动向 Spring 容器注册 SqlSessionFactory 作为 bean。
- 所需的部分配置项从 Spring 上下文中的环境变量获取,因此可以把配置添加到 application.perperties 文件中,参见 MybatisProperties 类。
- 所需的数据源为 Spring 上下文中的 DataSource bean。
- Spring 上下文中的部分 MyBatis 组件 bean 将添加到配置中,包括 Interceptor、DatabaseIdProvider、TypeHandler、LanguageDriver。
- 此外实现了 MyBatis ConfigurationCustomizer 接口的 bean 将被回调用于通过代码自定义配置。
- 自动向 Spring 容器注册 SqlSessionTemplate 作为 bean。
- 自动扫描添加了 @Mapper 注解的 Mapper 并向 Spring 注册 bean。
总结
MyBatis 为了整合 Spring 提出了两个项目:mybatis-spring 用于向 springframework 整合,此时需要手动配置 SqlSessionFactory 及 Mapper,mybatis-spring-boot-starter 用于向 springboot 整合,提供了从 spring 上下文收集配置自动创建 MyBatis bean 的能力。