spring boot(三) 集成mybatis
前言
还记得之前我们写接口也是基于SpringMVC+MyBatis环境下,项目入手就需要N个配置文件,N个步骤才能实现,不但繁琐,而且时间长了xml配置文件太多,难以维护。现在基于spring boot环境。3分钟就能编写一个基于MyBatis增删改查的demo。那么为什么这么神奇呢?
准备工作
1、环境依赖
junit junit 4.12 org.springframework.boot spring-boot 2.0.2.RELEASE org.springframework.boot spring-boot-starter-web 2.0.2.RELEASE org.springframework.boot spring-boot-starter-test 2.0.2.RELEASE org.springframework.boot spring-boot-starter 2.0.2.RELEASE org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.1 mysql mysql-connector-java 5.1.46
2、配置文件(applition.yml)
主要配置数据源(DataSource)和MyBatis。
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.80.129:3306/test username: root password: 123456 mybatis: type-aliases-package: com.zhangfei.entity mapper-Locations: classpath:mybatis/*.xml
Example
1、程序入口
这里最关键的地方就是MapperScan注解,扫码Mapper接口所在地址。
@SpringBootApplication @MapperScan(value = "com.zhangfei.dao") public class DemoApplication { public static void main(String[] args){ SpringApplication.run(DemoApplication.class,args); } }
2、Dao接口实现
也就是Mapper实现,放在resources/mybatis/*.xml
<?xml version="1.0" encoding="UTF-8"?>insert into student (name,age) values (#{name},#{age}) update student set name=#{name},age=#{age} where id=#{id} delete from student where id=#{id}
OK,有了接口、有了接口实现,第一个增删改查就搞定了,就是这么简单,来看单元测试。 单元测试注意2.x环境下已经推荐使用SpringBootTest注解。
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class DemoApplicationTest { @Autowired StudentDao studentDao; @Test public void test(){ Liststudents=studentDao.getStudentList(); System.out.println("size:"+students.size()); Student student=studentDao.getById(1); System.out.println(student.getId()+","+student.getName()); } @Test public void test1(){ //测试insert Student student=new Student(); student.setName("lisi"); student.setAge(30); int result=studentDao.insert(student); } @Test public void test2(){ //测试update Student student=new Student(); student.setName("lisi222"); student.setAge(35); student.setId(2); int result=studentDao.update(student); } @Test public void test3(){ //测试update long id=3; int result=studentDao.delete(id); } }
总结:
还记得之前用SpringMVC+MyBatis开发接口应用时,基于spring托管mybatis时需要在xml中配置datasource、sqlsessionfactory、sqlsession。那么现在不需要自己亲力亲为了呢,那么这就是springboot、mybatis自动配置的工作所在。 在org.mybatis.spring.boot.configure包下有两个关键类:
MyBatisProperties
这个类主要是读取你在applition.yml中配置的mybatis的基本属性用来配置数据源使用
@ConfigurationProperties( prefix = "mybatis" ) public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; private String configLocation; private String[] mapperLocations; private String typeAliasesPackage; private String typeHandlersPackage; private boolean checkConfigLocation = false; private ExecutorType executorType; private Properties configurationProperties; @NestedConfigurationProperty private Configuration configuration; public MybatisProperties() { } public String getConfigLocation() { return this.configLocation; } public void setConfigLocation(String configLocation) { this.configLocation = configLocation; } /** @deprecated */ @Deprecated public String getConfig() { return this.configLocation; } /** @deprecated */ @Deprecated public void setConfig(String config) { this.configLocation = config; } public String[] getMapperLocations() { return this.mapperLocations; } public void setMapperLocations(String[] mapperLocations) { this.mapperLocations = mapperLocations; } public String getTypeHandlersPackage() { return this.typeHandlersPackage; } public void setTypeHandlersPackage(String typeHandlersPackage) { this.typeHandlersPackage = typeHandlersPackage; } public String getTypeAliasesPackage() { return this.typeAliasesPackage; } public void setTypeAliasesPackage(String typeAliasesPackage) { this.typeAliasesPackage = typeAliasesPackage; } public boolean isCheckConfigLocation() { return this.checkConfigLocation; } public void setCheckConfigLocation(boolean checkConfigLocation) { this.checkConfigLocation = checkConfigLocation; } public ExecutorType getExecutorType() { return this.executorType; } public void setExecutorType(ExecutorType executorType) { this.executorType = executorType; } public Properties getConfigurationProperties() { return this.configurationProperties; } public void setConfigurationProperties(Properties configurationProperties) { this.configurationProperties = configurationProperties; } public Configuration getConfiguration() { return this.configuration; } public void setConfiguration(Configuration configuration) { this.configuration = configuration; } public Resource[] resolveMapperLocations() { PathMatchingResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); ArrayList resources = new ArrayList(); if(this.mapperLocations != null) { String[] var3 = this.mapperLocations; int var4 = var3.length; for(int var5 = 0; var5 < var4; ++var5) { String mapperLocation = var3[var5]; try { Resource[] mappers = resourceResolver.getResources(mapperLocation); resources.addAll(Arrays.asList(mappers)); } catch (IOException var8) { ; } } } return (Resource[])resources.toArray(new Resource[resources.size()]); } }
MyBatisAutoConfiguration
@Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnBean({DataSource.class}) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class}) public class MybatisAutoConfiguration { private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class); private final MybatisProperties properties; private final Interceptor[] interceptors; private final ResourceLoader resourceLoader; private final DatabaseIdProvider databaseIdProvider; private final ListconfigurationCustomizers; public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider databaseIdProvider, ObjectProvider > configurationCustomizersProvider) { this.properties = properties; this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable(); this.resourceLoader = resourceLoader; this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable(); this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable(); } @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)"); } } @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())); } org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration(); if(configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { configuration = new org.apache.ibatis.session.Configuration(); } if(configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { Iterator var4 = this.configurationCustomizers.iterator(); while(var4.hasNext()) { ConfigurationCustomizer customizer = (ConfigurationCustomizer)var4.next(); 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())) { factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if(StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if(!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } return factory.getObject(); } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); return executorType != null?new SqlSessionTemplate(sqlSessionFactory, executorType):new SqlSessionTemplate(sqlSessionFactory); } @Configuration @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class}) @ConditionalOnMissingBean({MapperFactoryBean.class}) public static class MapperScannerRegistrarNotFoundConfiguration { public MapperScannerRegistrarNotFoundConfiguration() { } @PostConstruct public void afterPropertiesSet() { MybatisAutoConfiguration.logger.debug("No {} found.", MapperFactoryBean.class.getName()); } } public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware { private BeanFactory beanFactory; private ResourceLoader resourceLoader; public AutoConfiguredMapperScannerRegistrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper"); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); try { if(this.resourceLoader != null) { scanner.setResourceLoader(this.resourceLoader); } List ex = AutoConfigurationPackages.get(this.beanFactory); if(MybatisAutoConfiguration.logger.isDebugEnabled()) { Iterator var5 = ex.iterator(); while(var5.hasNext()) { String pkg = (String)var5.next(); MybatisAutoConfiguration.logger.debug("Using auto-configuration base package \'{}\'", pkg); } } scanner.setAnnotationClass(Mapper.class); scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(ex)); } catch (IllegalStateException var7) { MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", var7); } } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } } }
这个类里的内容是不是看起来会面熟很多,看到了熟悉的SqlSessionFactory、SqlSessionTemplate。SpringBoot在运行的时候会进行自动配置,在mybatis-spring-boot-autoconfigure里找到spring.factories。然后找到MyBatisAutoConfiguration。其中checkConfigFileExists 使用了PostContruct注解,在初始化时加载mybatis配置文件创建SqlSessionFactory。sqlSessionFactory、sqlSessionTemplate两个方法都添加了ConditionalOnMissingBean注解,在bean不存在时分别创建SqlSessionFactory和SqlSessionTemplate。
在回过头来看程序入口还有一个MapperScan注解,主程序运行时去扫描mapper接口实现类,为其注入sqlsessionfactory和sqlsession。OK。这就是spring boot和mybatis为我们所提供的基础功能。