面試官問:Mybatis Plus 是如何實現(xiàn)動態(tài) SQL 語句的?原理你懂嗎?
相關閱讀:杭州程序員從互聯(lián)網(wǎng)跳央企,曬一天工作和收入,網(wǎng)友:待一年就廢
Mybatis-Plus(簡稱MP)是一個 Mybatis 的增強工具,那么它是怎么增強的呢?其實就是它已經(jīng)封裝好了一些crud方法,開發(fā)就不需要再寫xml了,直接調(diào)用這些方法就行,就類似于JPA。 那么這篇文章就來閱讀以下MP的具體實現(xiàn),看看是怎樣實現(xiàn)這些增強的。
入口類:MybatisSqlSessionFactoryBuilder
通過在入口類 MybatisSqlSessionFactoryBuilder#build方法中, 在應用啟動時, 將mybatis plus(簡稱MP)自定義的動態(tài)配置xml文件注入到Mybatis中。
public class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder {public SqlSessionFactory build(Configuration configuration) {// ... 省略若干行if (globalConfig.isEnableSqlRunner()) {new SqlRunnerInjector().inject(configuration);}// ... 省略若干行return sqlSessionFactory;}}
這里涉及到2個MP2個功能類
擴展繼承自Mybatis的MybatisConfiguration類: MP動態(tài)腳本構建,注冊,及其它邏輯判斷。搜索公眾號互聯(lián)網(wǎng)架構師復“2T”,送你一份驚喜禮包。 SqlRunnerInjector: MP默認插入一些動態(tài)方法的xml 腳本方法。
public class MybatisConfiguration extends Configuration {/*** Mapper 注冊*/protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);// ..../*** 初始化調(diào)用*/public MybatisConfiguration() {super();this.mapUnderscoreToCamelCase = true;languageRegistry.setDefaultDriverClass(MybatisXMLLanguageDriver.class);}/*** MybatisPlus 加載 SQL 順序:* <p> 1、加載 XML中的 SQL </p>* <p> 2、加載 SqlProvider 中的 SQL </p>* <p> 3、XmlSql 與 SqlProvider不能包含相同的 SQL </p>* <p>調(diào)整后的 SQL優(yōu)先級:XmlSql > sqlProvider > CurdSql </p>*/@Overridepublic void addMappedStatement(MappedStatement ms) {// ...}// ... 省略若干行/*** 使用自己的 MybatisMapperRegistry*/@Overridepublic <T> void addMapper(Class<T> type) {mybatisMapperRegistry.addMapper(type);}// .... 省略若干行}
在MybatisMapperRegistry中,MP將mybatis的MapperAnnotationBuilder替換為MP自己的MybatisMapperAnnotationBuilder
public class MybatisMapperRegistry extends MapperRegistry {public <T> void addMapper(Class<T> type) {// ... 省略若干行MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);parser.parse();// ... 省略若干行}}
public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {@Overrdepublic void parse() {//... 省略若干行for (Method method : type.getMethods()) {/** for循環(huán)代碼, MP判斷method方法是否是@Select @Insert等mybatis注解方法**/parseStatement(method);InterceptorIgnoreHelper.initSqlParserInfoCache(cache, mapperName, method);SqlParserHelper.initSqlParserInfoCache(mapperName, method);}/** 這2行代碼, MP注入默認的方法列表**/if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);}//... 省略若干行}@Overridepublic void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {Class<?> modelClass = extractModelClass(mapperClass);//... 省略若干行List<AbstractMethod> methodList = this.getMethodList(mapperClass);TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);// 循環(huán)注入自定義方法methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));mapperRegistryCache.add(className);}}public class DefaultSqlInjector extends AbstractSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {return Stream.of(new Insert(),//... 省略若干行new SelectPage()).collect(toList());}}
以 SelectById 這個類為例說明下
/*** 根據(jù)ID 查詢一條數(shù)據(jù)*/public class SelectById extends AbstractMethod {public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {/** 定義 mybatis xml method id, 對應 <id="xyz"> **/SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;/** 構造id對應的具體xml片段 **/SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),sqlSelectColumns(tableInfo, false),tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),tableInfo.getLogicDeleteSql(true, true)), Object.class);/** 將xml method方法添加到mybatis的MappedStatement中 **/return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);}}

public class YourSqlSessionFactoryBean extends SqlSessionFactoryBean implements ApplicationContextAware {private Resource[] mapperLocations;public void setMapperLocations(Resource... mapperLocations) {super.setMapperLocations(mapperLocations);/** 存使用mybatis原生定義的mapper xml文件路徑**/this.mapperLocations = mapperLocations;}/*** {@inheritDoc}*/public void afterPropertiesSet() throws Exception {ConfigurableListableBeanFactory beanFactory = getBeanFactory();/** 只需要通過將自定義的方法構造成xml resource和原生定義的Resource一起注入到mybatis中即可, 這樣就可以實現(xiàn)MP的自定義動態(tài)SQL和原生SQL的共生關系**/this.setMapperLocations(InjectMapper.getMapperResource(this.dbType, beanFactory, this.mapperLocations));super.afterPropertiesSet();}}
評論
圖片
表情

