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(){

        List students=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 List configurationCustomizers;

    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为我们所提供的基础功能。