mybatis与springboot的集成

  本篇主要讲一些源码层面的,具体的使用就不赘述了。

一、spring与mybatis的集成

  在讲自动装配出现的原因时,有提到自动装配是为了解放繁琐的spring配置,所以要了解一个框架被springboot的管理,我们就需要从spring与该框架的集成开始说起。

  mybatis与spring的集成涉及这些关键类:SqlSessionFactoryBean(用于产生SqlSessionFactory,主要对配置文件等配置进行解析)、SqlSessionTemplate(spring中的sqlSession,是我们进行crud操作的源头)、MapperScanerConfigurer(mapper的自动扫描)。这三个类相互关联,其中sqlSessionTemplate可以不配!接下来,在讲sb与mybatis的集成时,我们会看到这三个熟悉的类的。如果要使用mapper,直接autowired即可,因为scanner已经注册了这些bean。

<!-- 依赖数据源、配置文件(可以自己配)、mapper文件地址等 -->
<
bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath*:mapper/*.xml"/> <property name="configLocation" value="classpath:mybatis.xml"/> </bean>
// 通过sqlSessionFactory的openSession获取template
<bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="mapper/*.xml"/>
        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
</bean>

 

二、springboot与mybatis的集成

  需要的启动器:mybatis-spring-boot-starter

  按照之前整理数据源的逻辑,我们仍然走这一套路:xxxAutoConfiguraton -> xxxProperties -> 在AutoConfiguraion中找@Bean或者@Configuration

@Configuration
// session是mybatis的绝对核心,所以必须有这些类
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
// 必须有数据源
@ConditionalOnSingleCandidate(DataSource.class)
// 要配置参数去找这个类
@EnableConfigurationProperties({MybatisProperties.class})
// 需要在这些装配类之后进行
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {}

  上面的自动装配类还是熟悉的配方,然后我们去看看MybatisProperties的属性吧。

// 要去管理mybatis就去设置mybatis.xxx
@ConfigurationProperties(
    prefix = "mybatis"
)
public class MybatisProperties {
    public static final String MYBATIS_PREFIX = "mybatis";
    private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
    private String configLocation;
    private String[] mapperLocations;
    private String typeAliasesPackage;
}

  接下来去找第一部分中提到的类。

   @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())); }
// 这个方法挺重要的,主要做了这么几件事来保证配置的顺利进行
// springboot中有配置,就装配configuration
// 如果既没有sb配置,又没有configLocation,那么就使用默认的configuration
// 如果只有configLocation,就绝对不会产生location,避免了后面的报错
// 这个方法还提到了,通过customizer去配置
this.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); } if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } Set<String> factoryPropertyNames = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet()); Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) { factory.setScriptingLanguageDrivers(this.languageDrivers); if (defaultLanguageDriver == null && this.languageDrivers.length == 1) { defaultLanguageDriver = this.languageDrivers[0].getClass(); } } if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) { factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver); }
     // factorybean获取factory的方式
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);
}

  还有一个关于mapper注册的,还不太明白,有机会补上!

 

三、在看源码时,发现的一些关于mybatis配置的问题

  Configuration是mybatis中非常重要的类,包含了mybatis的配置内容,在最原生的mybatis配置文件中,最顶层的标签对就是<Configuration></Configuration>,里面可以配置setting、别名、enviroment、mapper等。到了spring和mybatis的集成时,我们既可以指定配置文件位置ConfigLocation,又可以在配置SqlSessionFactoryBean中显式指定(能不能重用还没看细节源码,但一般配置xml文件更好,简单明了!)。到了SpringBoot中,我们既可以有两个选择:①、指定ConfigLocation;②、配置mybatis.configuraton.xxx。

  注意 sb中,不能同时使用两个设置去配configuration,会报错,这里的报错代码也贴出来看看,为什么不允许这么做?我当时寻找的思路:首先负责configuration的主要时SqlSessionFactoryBean对象,第一直觉去看了AutoConfiguration中获取SqlSessionFactoryBean的过程中是否有相应的内容,但是没有。于是我又去找getObject方法,寻找代码点进去就发现了问题。直接看代码。

 

@Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

// 这里的configuration是从sb配置文件中获得的。
// 可以看到,不能两个都为null, 也不能两个都不能null
// 否则会报错
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
        "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

 

  所以,别整花里胡哨的乱炖行为,乖乖使用配置文件,挺好!

 

posted @ 2021-02-28 12:57  4verson  阅读(162)  评论(0)    收藏  举报