Mybatis?Plus使用XML編寫(xiě)動(dòng)態(tài)sql的超簡(jiǎn)易方法
使用xml編寫(xiě)動(dòng)態(tài)sql
在Resources文件夾下創(chuàng)建一個(gè)Mapper文件夾
比如我們需要在User表中使用增刪改查,創(chuàng)建UserMapper.xml,對(duì)應(yīng)MybatisPlus中的UserMapper接口
之后我們?cè)赼pplication.yml中配置mapper文件夾的路徑
mybatis-plus: ? mapper-locations: classpath:mapper/*.xml
之后在UserMapper中創(chuàng)建函數(shù)
@Repository public interface UserMapper extends BaseMapper<User> { ?? ??? ?// 使函數(shù)參數(shù)對(duì)應(yīng)xml中的參數(shù)wxNickName ? ? List<User> selectByName(@Param("wxNickName") String name); }
就可以在UserMapper.xml中寫(xiě)sql語(yǔ)句了
寫(xiě)法和Mybatis一樣滴
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "<http://mybatis.org/dtd/mybatis-3-mapper.dtd>"> <mapper namespace="com.zhou.usercenter.dao.user.UserMapper"> ? ? <select id="selectByName" resultType="com.zhou.usercenter.domain.entity.user.User"> ? ? ? ? select * from user ? ? ? ? <where> ? ? ? ? ? ? <if test="wxNickName != null and wxNickName != ''"> ? ? ? ? ? ? ? ? wx_nickname like CONCAT('%',#{wxNickName},'%'); ? ? ? ? ? ? </if> ? ? ? ? </where> ? ? </select> </mapper>
之后調(diào)用即可
@SpringBootTest class UserCenterApplicationTests { ? ? @Autowired ? ? UserMapper userMapper; ? ? @Test ? ? void contextLoads() { ? ? ? ? List<User> users = userMapper.selectByName("杰倫"); ? ? ? ? System.out.println(users); ? ? } }
即可看到正確的輸出結(jié)果
動(dòng)態(tài)SQL語(yǔ)句的原理
Mybatis-Plus(簡(jiǎn)稱(chēng)MP)是一個(gè) Mybatis 的增強(qiáng)工具,那么它是怎么增強(qiáng)的呢?其實(shí)就是它已經(jīng)封裝好了一些crud方法,開(kāi)發(fā)就不需要再寫(xiě)xml了,直接調(diào)用這些方法就行,就類(lèi)似于JPA。那么這篇文章就來(lái)閱讀以下MP的具體實(shí)現(xiàn),看看是怎樣實(shí)現(xiàn)這些增強(qiáng)的。
入口類(lèi):MybatisSqlSessionFactoryBuilder
通過(guò)在入口類(lèi) MybatisSqlSessionFactoryBuilder#build方法中, 在應(yīng)用啟動(dòng)時(shí), 將mybatis plus(簡(jiǎn)稱(chēng)MP)自定義的動(dòng)態(tài)配置xml文件注入到Mybatis中。
public class MybatisSqlSessionFactoryBuilder extends SqlSessionFactoryBuilder { public SqlSessionFactory build(Configuration configuration) { // ... 省略若干行 if (globalConfig.isEnableSqlRunner()) { new SqlRunnerInjector().inject(configuration); } // ... 省略若干行 return sqlSessionFactory; } }
這里涉及到2個(gè)MP2個(gè)功能類(lèi)
擴(kuò)展繼承自Mybatis的MybatisConfiguration類(lèi): MP動(dòng)態(tài)腳本構(gòu)建,注冊(cè),及其它邏輯判斷。
SqlRunnerInjector: MP默認(rèn)插入一些動(dòng)態(tài)方法的xml 腳本方法。
MybatisConfiguration類(lèi)
這里我們重點(diǎn)剖析MybatisConfiguration類(lèi),在MybatisConfiguration中,MP初始化了其自身的MybatisMapperRegistry,而MybatisMapperRegistry是MP加載自定義的SQL方法的注冊(cè)器。
MybatisConfiguration中很多方法是使用MybatisMapperRegistry進(jìn)行重寫(xiě)實(shí)現(xiàn)
其中有3個(gè)重載方法addMapper實(shí)現(xiàn)了注冊(cè)MP動(dòng)態(tài)腳本的功能。
public class MybatisConfiguration extends Configuration { /** * Mapper 注冊(cè) */ 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)先級(jí):XmlSql > sqlProvider > CurdSql </p> */ @Override public void addMappedStatement(MappedStatement ms) { // ... } // ... 省略若干行 /** * 使用自己的 MybatisMapperRegistry */ @Override public <T> void addMapper(Class<T> type) { mybatisMapperRegistry.addMapper(type); } // .... 省略若干行 }
在MybatisMapperRegistry中,MP將mybatis的MapperAnnotationBuilder替換為MP自己的MybatisMapperAnnotationBuilder
public class MybatisMapperRegistry extends MapperRegistry { @Override public <T> void addMapper(Class<T> type) { // ... 省略若干行 MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type); parser.parse(); // ... 省略若干行 } }
在MybatisMapperRegistry類(lèi)的addMapper方法中,真正進(jìn)入到MP的核心類(lèi)MybatisMapperAnnotationBuilder,MybatisMapperAnnotationBuilder這個(gè)類(lèi)是MP實(shí)現(xiàn)動(dòng)態(tài)腳本的關(guān)鍵類(lèi)。
MybatisMapperAnnotationBuilder動(dòng)態(tài)構(gòu)造
在MP的核心類(lèi)MybatisMapperAnnotationBuilder的parser方法中,MP逐一遍歷要加載的Mapper類(lèi),加載的方法包括下面幾個(gè)
public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder { @Override public 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注入默認(rèn)的方法列表**/ if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) { GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type); } //... 省略若干行 } @Override public 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 { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { return Stream.of( new Insert(), //... 省略若干行 new SelectPage() ).collect(toList()); } }
在MybatisMapperAnnotationBuilder中,MP真正將框架自定義的動(dòng)態(tài)SQL語(yǔ)句注冊(cè)到Mybatis引擎中。而AbstractMethod則履行了具體方法的SQL語(yǔ)句構(gòu)造。
具體的AbstractMethod實(shí)例類(lèi),構(gòu)造具體的方法SQL語(yǔ)句
以 SelectById 這個(gè)類(lèi)為例說(shuō)明下
/** * 根據(jù)ID 查詢(xún)一條數(shù)據(jù) */ public class SelectById extends AbstractMethod { @Override public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { /** 定義 mybatis xml method id, 對(duì)應(yīng) <id="xyz"> **/ SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID; /** 構(gòu)造id對(duì)應(yīng)的具體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); } }
至此,MP完成了在啟動(dòng)時(shí)加載自定義的方法xml配置的過(guò)程,后面的就是mybatis ${變量} #{變量}的動(dòng)態(tài)替換和預(yù)編譯,已經(jīng)進(jìn)入mybatis自有功能。
小結(jié)一下
MP總共改寫(xiě)和替換了mybatis的十多個(gè)類(lèi),主要如下圖所示:
總體上來(lái)說(shuō),MP實(shí)現(xiàn)mybatis的增強(qiáng),手段略顯繁瑣和不夠直觀,其實(shí)根據(jù)MybatisMapperAnnotationBuilder構(gòu)造出自定義方法的xml文件,將其轉(zhuǎn)換為mybatis的Resource資源,可以只繼承重寫(xiě)一個(gè)Mybatis類(lèi):SqlSessionFactoryBean 比如如下:
public class YourSqlSessionFactoryBean extends SqlSessionFactoryBean implements ApplicationContextAware { private Resource[] mapperLocations; @Override public void setMapperLocations(Resource... mapperLocations) { super.setMapperLocations(mapperLocations); /** 暫存使用mybatis原生定義的mapper xml文件路徑**/ this.mapperLocations = mapperLocations; } /** * {@inheritDoc} */ @Override public void afterPropertiesSet() throws Exception { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); /** 只需要通過(guò)將自定義的方法構(gòu)造成xml resource和原生定義的Resource一起注入到mybatis中即可, 這樣就可以實(shí)現(xiàn)MP的自定義動(dòng)態(tài)SQL和原生SQL的共生關(guān)系**/ this.setMapperLocations(InjectMapper.getMapperResource(this.dbType, beanFactory, this.mapperLocations)); super.afterPropertiesSet(); } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- MyBatis在注解上使用動(dòng)態(tài)SQL方式(@select使用if)
- mybatis的動(dòng)態(tài)sql之if test的使用說(shuō)明
- Spring Boot整合mybatis使用注解實(shí)現(xiàn)動(dòng)態(tài)Sql、參數(shù)傳遞等常用操作(實(shí)現(xiàn)方法)
- Mybatis之動(dòng)態(tài)sql標(biāo)簽的使用
- Mybatis中動(dòng)態(tài)SQL,if,where,foreach的使用教程詳解
- Mybatis之動(dòng)態(tài)SQL使用小結(jié)(全網(wǎng)最新)
相關(guān)文章
SpringBoot 配合 SpringSecurity 實(shí)現(xiàn)自動(dòng)登錄功能的代碼
這篇文章主要介紹了SpringBoot 配合 SpringSecurity 實(shí)現(xiàn)自動(dòng)登錄功能的代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09基于Java解決華為機(jī)試實(shí)現(xiàn)密碼截取?
這篇文章主要介紹了基于Java解決華為機(jī)試實(shí)現(xiàn)密碼截取,文章圍繞主題相關(guān)資料展開(kāi)詳細(xì)內(nèi)容,具有一的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你有所幫助2022-02-02淺談Java8 的foreach跳出循環(huán)break/return
這篇文章主要介紹了Java8 的foreach跳出循環(huán)break/return,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot如何通過(guò)yml方式整合Mybatis
這篇文章主要介紹了SpringBoot如何通過(guò)yml方式整合Mybatis,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04使用注解開(kāi)發(fā)SpringMVC詳細(xì)配置教程
這篇文章主要介紹了使用注解開(kāi)發(fā)SpringMVC詳細(xì)配置教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Java并發(fā)實(shí)例之CyclicBarrier的使用
這篇文章主要介紹了Java并發(fā)實(shí)例之CyclicBarrier的使用,涉及CyclicBarrier的介紹,以及相關(guān)的并發(fā)實(shí)例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11在Mybatis中association標(biāo)簽多層嵌套的問(wèn)題
這篇文章主要介紹了在Mybatis中association標(biāo)簽多層嵌套的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03命令行中 javac、java、javap 的使用小結(jié)
使用 java 命令運(yùn)行一個(gè).class文件,需要使用該類(lèi)的全限定類(lèi)名,同時(shí)需要在當(dāng)前路徑下有該類(lèi)的包層次文件夾,這篇文章主要介紹了命令行中 javac、java、javap 的使用小結(jié),需要的朋友可以參考下2023-07-07java讀取文件內(nèi)容,解析Json格式數(shù)據(jù)方式
這篇文章主要介紹了java讀取文件內(nèi)容,解析Json格式數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09