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(); }
所以,别整花里胡哨的乱炖行为,乖乖使用配置文件,挺好!
浙公网安备 33010602011771号