在实际项目中,应该很少有不使用数据库的项目吧,目前来看,大部分的公司使用的都是mysql+mybaties做持久化存储。因此,在这里为大家讲解一下springboot与mybaties应该如何整合。以下内容纯属个人见解,若有不对的地方,望大家指正。
项目搭建
通过第一篇文章,我们已经能够很熟练的搭建springboot项目了,这里,我们就减少项目搭建的步骤,若大家不会搭建,可以返回第一篇文章了解。要想与mybaties进行整合,我们需要添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--用于减少get set等,加速开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
springboot在启动的时候,会自动化配置mybaties相关信息,但是前提下,我们需要配置数据源信息,否则会抛出异常。如下所示:
#数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
至于这里的driver,我们要使用com.mysql.cj.jdbc.Driver,是因为我们使用的是最新的mysql驱动,com.mysql.jdbc.Driver也能够使用,只是在项目启动的时候,会抛出警告。
好了,现在我们已经写好数据源配置,接下来,我们就来写一个示例,来验证是否能够使用。
User:
@Data
public class User {
private Long id;
private String name;
private int age;
}
UserMapper:
@Repository
public interface UserMapper {
/**
* 查询所有用户
* @return list
*/
List<User> selectAll();
}
关于mybaties的使用方式,一般分为两种:注解和XML配置。下面分别介绍一下,方便大家回顾:
XML配置
在使用XML配置的时候,需要注意,我们要在配置文件中加上如下配置:
#mybatis配置
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
mybatis.type-aliases-package=com.fanqie.springboot2.entity
- mapper-locations:指定Mapper.xml文件的位置,以供springboot扫描。
- type-aliases-package:使用别名。方便开发,不是必须的。若不提供该配置,我们就需要指定全限定名或者配置ResultMap。
UserMapper.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fanqie.springboot2.mapper.UserMapper">
<select id="selectAll" resultType="user">
select * from user
</select>
</mapper>
注解
使用注解的形式就相对比较简单了,我们不需要提供XML文件,也不需要指定别名。应该如何使用呢?请看下面:
@Repository
public interface UserMapper {
/**
* 查询所有用户
* @return list
*/
@Select("select * from user")
List<User> selectAll();
}
我们可以使用注解的形式进行配置,不需要额外的XML文件的配置,方便,快速。支持select、update、insert、delete。
下面我们就写一个接口来调用,看是否配置成功。这里我们省略service层,如下所示:
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 注入userMapper
*/
private final UserMapper userMapper;
@Autowired
public UserController(UserMapper userMapper) {
this.userMapper = userMapper;
}
@GetMapping("/all")
public List<User> selectAll(){
return this.userMapper.selectAll();
}
}
大家注意了,在使用Mybaties的时候,千万不要漏掉一步,那就是在启动类上加上Mapper扫描注解,以免mapper类找不到。如下所示:
@SpringBootApplication
@MapperScan("com.fanqie.springboot2.mapper")
public class MybatiesApplication {
public static void main(String[] args) {
SpringApplication.run(MybatiesApplication.class, args);
}
}
启动项目,访问结果如下:
初步整合,就算完成了,如果大家想要看到查询过程中sql语句,参数,以及查询结果,可以在配置文件中加上如下配置:
logging.level.com.fanqie.springboot2.mapper:debug
level后面跟的是包路径,mapper目录下使用debug模式,就可以看到sql相关信息,具体效果,大家自行观察。
源码分析
在分析源码之前,先来了解几个注解,对分析源码特别重要:
- @ConditionalOnClass:表明注解后的参数类必须存在,否则不会实例化该注解修饰的类或Bean。
- @ConditionalOnBean:表明注解后的参数类必须在上下文存在Bean对象,否则不会实例化该注解修饰的类或Bean。
- @ConditionalOnMissingBean:当上下文不存在该注解修饰的Bean对象,才会实例化该Bean。
- @ConfigurationProperties:用于加载配置。当application.properties或者application.yml中有属性配置,可以通过该注解去加载,例如:user.name=张三,可以在user类上添加@ConfigurationProperties(prefix = "user")来使用。@ConfigurationProperties通常用于对象级别的资源加载,若单个字段的属性加载,使用@Value即可。
- @EnableConfigurationProperties:该注解一般与@ConfigurationProperties配合使用,表明开启对@ConfigurationProperties注解的支持。
- @AutoConfigureAfter:表明在自动配置该注解所使用的参数类之后再来加载当前类。
使用过spring-mybatie的伙伴,应该知道在使用的过程中,我们都需要配置DataSource、SqlSessionFactoryBean配置sqlSessionFactory、MapperScannerConfigurer配置自动扫描接口包路径等。现在,我们来看看springboot是如何自动配置这些Bean,如何找到自动配置的类在哪里呢?如下图:
MybatisAutoConfiguration就是自动配置类,部分内容如下:
/**
*该类用于springboot自动配置mybaties
*通过类注解,我们可以发现,自动配置有限定条件:上下文必须存在SqlSessionFactory.class、SqlSessionFactoryBean.class、
*DataSource.clas的Bean对象,mybaties的配置从MybatisProperties(加载前缀为mybatis的属性)配置类中获取
*该类仅会在DataSourceAutoConfiguration.class自动配置之后实例化
*/
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
/**
*在实例化之前执行,校验是否存在mybaties配置文件
*/
@PostConstruct
public void checkConfigFileExists() {
if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
Assert.state(resource.exists(), "Cannot find config location: " + resource
+ " (please add config file or check your Mybatis configuration)");
}
}
/**
*若上下文不存在SqlSessionFactory,则进行实例化
*需要上下文存在DataSource
*/
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
//注入数据源
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
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())) {
//向SqlSessionFactoryBean注入需要使用别名的Entity包路径
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
//向SqlSessionFactoryBean注入mapper.xml文件包路径
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
return factory.getObject();
}
/**
*若上下文不存在SqlSessionTemplate,则进行实例化
*需要上下文存在SqlSessionFactory
*/
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
}
/**
* 该类用于自动扫描被@Mapper所修饰的Mapper接口
* 在springboot启动类上,通过@MapperScan("com.fanqie.springboot2.mapper")即可使用
*/
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware
分析发现,我们没有看到DataSource是如何自动配置的,大家请注意该类上有这样的一个注解@AutoConfigureAfter(DataSourceAutoConfiguration.class),表明在实例化MybatisAutoConfiguration时,DataSource已经自动配置了,我们来看看里面的具体内容:
//DataSourceProperties加载的就是application.properties中配置的以spring.datasource为前缀的参数
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
/**
* 配置嵌入式数据源
*/
@Configuration
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
/**
* 配置连接池数据源,支持Hikari、Tomcat、Dbcp2、Generic、Jmx
* 默认使用HikariDataSource
*/
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
}
在SpringBoot2中默认使用Hikari连接池,相应的使用HikariDataSource数据源,若大家不想使用Hikari连接池,可以按照自己项目的需要进行配置,示例如下:
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
大家或许会问,我是如何知道默认采用的是HikariDataSource,其实所有的信息都会在日志中输出,只是默认使用的是INFO级别的日志,看不到Debug信息,所以大家在调试的时候,不妨在application.properties添加如下配置:
logging.level.root:debug
这时大家就可以在日志中搜索自己想要的内容,例如这里的DataSource:
2019-03-31 19:36:51.180 DEBUG 16692 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'dataSource'
2019-03-31 19:36:51.180 DEBUG 16692 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari'
2019-03-31 19:36:51.181 DEBUG 16692 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties'
mybaties的基础部分就描述到这里了,相关信息不难,大家可以亲自调试代码,才能有更深的了解。
总结
SpringBoot2与Mybaties整合,需要配置的内容变得非常的少,大家只需要配置数据库连接信息,基本上就可以很好的工作。支持注解和XML两种Mapper方式,可以任选其一。SpringBoot2中默认使用Hikari连接池,至于具体的好处还不是很清楚,只是说性能更好,至于具体使用哪种连接池,大家可以根据项目需要自行配置。
源码可见https://gitee.com/hpaw/SpringBoot2Demo/tree/master