無(wú)需 XML Mapper,超級(jí) Mybatis 代碼即是 SQL 操作!真香?
責(zé)編:架構(gòu)君?|?來(lái)源:JavaGuide
Fluent Mybatis 介紹 Fluent Mybatis 特性一覽 Fluent Mybatis 原理 Fluent Mybatis vs Mybatis vs Mybatis Plus
實(shí)現(xiàn)需求比較 生成代碼編碼比較 三者對(duì)比總結(jié) Fluent Mybatis 實(shí)戰(zhàn)
引入依賴 創(chuàng)建表 創(chuàng)建數(shù)據(jù)庫(kù)表對(duì)應(yīng)的 Entity 類 配置數(shù)據(jù)源 測(cè)試 總結(jié) 最近看到一個(gè) ORM 框架 Fluent Mybatis 挺有意思的,整個(gè)設(shè)計(jì)理念非常符合工程師思維。
我對(duì)官方文檔的部分內(nèi)容進(jìn)行了簡(jiǎn)單整理,通過(guò)這篇文章帶你看看這個(gè)新晉 ORM 框架。
官方文檔:https://gitee.com/fluent-mybatis/fluent-mybatis/wikis
提前聲明一下:對(duì)于這類個(gè)人維護(hù)和開發(fā)的框架,如果沒(méi)有充分的了解,一定一定一定不要用在正式的項(xiàng)目上!不然后續(xù)遇到問(wèn)題會(huì)很麻煩的?。?!我目前對(duì)于 Fluent Mybatis 這個(gè)框架也僅僅是感興趣,想要學(xué)習(xí)一下它的內(nèi)部設(shè)計(jì)。
Fluent Mybatis 介紹
何為 Fluent Mybatis??Fluent Mybatis, 是一款 Mybatis 語(yǔ)法增強(qiáng)框架, 綜合了 Mybatis Plus, Dynamic SQL, JPA 等框架特性和優(yōu)點(diǎn), 利用 annotation processor 生成代碼。
Fluent Mybatis 有什么亮點(diǎn)??使用 Fluent Mybatis 可以不用寫具體的 XML 文件,通過(guò) Java API 可以構(gòu)造出比較復(fù)雜的業(yè)務(wù) SQL 語(yǔ)句,做到代碼邏輯和 SQL 邏輯的合一。不再需要在 Dao 中組裝查詢或更新操作,在 XML 或 Mapper 中再組裝參數(shù)。
項(xiàng)目地址:https://gitee.com/fluent-mybatis/fluent-mybatis
系列文章:https://juejin.cn/column/7033388011911921678
基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能。
項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
Fluent Mybatis 特性一覽
Fluent Mybatis 特性
基于微服務(wù)的思想,構(gòu)建在 B2C 電商場(chǎng)景下的項(xiàng)目實(shí)戰(zhàn)。核心技術(shù)棧,是 Spring Boot + Dubbo 。未來(lái),會(huì)重構(gòu)成 Spring Cloud Alibaba 。
項(xiàng)目地址:https://github.com/YunaiV/onemall
Fluent Mybatis 原理
Fluent Mybatis 原理
Fluent Mybatis vs Mybatis vs Mybatis Plus
對(duì)比原生 Mybatis, Mybatis Plus 或者其他框架,F(xiàn)luentMybatis 提供了哪些便利呢?
實(shí)現(xiàn)需求比較
我們通過(guò)一個(gè)比較典型的業(yè)務(wù)需求來(lái)具體實(shí)現(xiàn)和對(duì)比下,假如有學(xué)生成績(jī)表結(jié)構(gòu)如下:
create?table?`student_score`
(
????id???????????bigint?auto_increment?comment?'主鍵ID'?primary?key,
????student_id???bigint????????????not?null?comment?'學(xué)號(hào)',
????gender_man???tinyint?default?0?not?null?comment?'性別,?0:女;?1:男',
????school_term??int???????????????null?comment?'學(xué)期',
????subject??????varchar(30)???????null?comment?'學(xué)科',
????score????????int???????????????null?comment?'成績(jī)',
????gmt_create???datetime??????????not?null?comment?'記錄創(chuàng)建時(shí)間',
????gmt_modified?datetime??????????not?null?comment?'記錄最后修改時(shí)間',
????is_deleted???tinyint?default?0?not?null?comment?'邏輯刪除標(biāo)識(shí)'
)?engine?=?InnoDB?default?charset=utf8;現(xiàn)在有需求:?統(tǒng)計(jì) 2000 年三門學(xué)科('英語(yǔ)', '數(shù)學(xué)', '語(yǔ)文')及格分?jǐn)?shù)按學(xué)期,學(xué)科統(tǒng)計(jì)最低分,最高分和平均分, 且樣本數(shù)需要大于 1 條,統(tǒng)計(jì)結(jié)果按學(xué)期和學(xué)科排序
我們可以寫 SQL 語(yǔ)句如下:
select?school_term,
???????subject,
???????count(score)?as?count,
???????min(score)???as?min_score,
???????max(score)???as?max_score,
???????avg(score)???as?max_score
from?student_score
where?school_term?>=?2000
??and?subject?in?('英語(yǔ)',?'數(shù)學(xué)',?'語(yǔ)文')
??and?score?>=?60
??and?is_deleted?=?0
group?by?school_term,?subject
having?count(score)?>?1
order?by?school_term,?subject;那上面的需求,分別用 Fluent Mybatis, 原生 Mybatis 和 Mybatis Plus 來(lái)實(shí)現(xiàn)一番。擴(kuò)展:一個(gè)很酷的快速開發(fā)代碼生成器系統(tǒng)
使用 Fluent Mybatis 來(lái)實(shí)現(xiàn)上面的功能?:
代碼地址:https://gitee.com/fluent-Mybatis/fluent-Mybatis-docs/tree/master/spring-boot-demo/
我們可以看到 fluent api 的能力,以及 IDE 對(duì)代碼的渲染效果。另外,搜索公眾號(hào)技術(shù)社區(qū)后臺(tái)回復(fù)“私活”,獲取一份驚喜禮包。
換成 Mybatis 原生實(shí)現(xiàn)上面的功能?:
1、定義?
Mapper?接口public?interface?MyStudentScoreMapper?{
????List2、定義接口需要用到的參數(shù)實(shí)體?
SummaryQuery@Data
@Accessors(chain?=?true)
public?class?SummaryQuery?{
????private?Integer?schoolTerm;
????private?List?subjects;
????private?Integer?score;
????private?Integer?minCount;
}3、定義實(shí)現(xiàn)業(yè)務(wù)邏輯的?
mapper xml?文件4、實(shí)現(xiàn)業(yè)務(wù)接口(這里是測(cè)試類, 實(shí)際應(yīng)用中應(yīng)該對(duì)應(yīng) Dao 類)
@RunWith(SpringRunner.class)
@SpringBootTest(classes?=?QuickStartApplication.class)
public?class?MybatisDemo?{
????@Autowired
????private?MyStudentScoreMapper?mapper;
????@Test
????public?void?Mybatis_demo()?{
????????//?構(gòu)造查詢參數(shù)
????????SummaryQuery?paras?=?new?SummaryQuery()
????????????.setSchoolTerm(2000)
????????????.setSubjects(Arrays.asList("英語(yǔ)",?"數(shù)學(xué)",?"語(yǔ)文"))
????????????.setScore(60)
????????????.setMinCount(1);
????????List總之,直接使用 Mybatis,實(shí)現(xiàn)步驟還是相當(dāng)?shù)姆爆?,效率太低。那換成 Mybatis Plus 的效果怎樣呢?
換成 Mybatis Plus 實(shí)現(xiàn)上面的功能?:
Mybatis Plus 的實(shí)現(xiàn)比 Mybatis 會(huì)簡(jiǎn)單比較多,實(shí)現(xiàn)效果如下
如紅框圈出的,寫 Mybatis Plus 實(shí)現(xiàn)用到了比較多字符串的硬編碼(可以用 Entity 的 get lambda 方法部分代替字符串編碼)。字符串的硬編碼,會(huì)給開發(fā)同學(xué)造成不小的使用門檻,個(gè)人覺(jué)的主要有 2 點(diǎn):
字段名稱的記憶和敲碼困難 Entity 屬性跟隨數(shù)據(jù)庫(kù)字段發(fā)生變更后的運(yùn)行時(shí)錯(cuò)誤 其他框架,比如 TkMybatis 在封裝和易用性上比 Mybatis Plus 要弱,就不再比較了。
生成代碼編碼比較
Fluent Mybatis 生成代碼設(shè)置?:
public?class?AppEntityGenerator?{
????static?final?String?url?=?"jdbc:mysql://localhost:3306/fluent_Mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8";
????public?static?void?main(String[]?args)?{
????????FileGenerator.build(Abc.class);
????}
????@Tables(
????????/**?數(shù)據(jù)庫(kù)連接信息?**/
????????url?=?url,?username?=?"root",?password?=?"password",
????????/**?Entity類parent?package路徑?**/
????????basePack?=?"cn.org.fluent.Mybatis.springboot.demo",
????????/**?Entity代碼源目錄?**/
????????srcDir?=?"spring-boot-demo/src/main/java",
????????/**?Dao代碼源目錄?**/
????????daoDir?=?"spring-boot-demo/src/main/java",
????????/**?如果表定義記錄創(chuàng)建,記錄修改,邏輯刪除字段?**/
????????gmtCreated?=?"gmt_create",?gmtModified?=?"gmt_modified",?logicDeleted?=?"is_deleted",
????????/**?需要生成文件的表?(?表名稱:對(duì)應(yīng)的Entity名稱?)?**/
????????tables?=?@Table(value?=?{"student_score"})
????)
????static?class?Abc?{
????}
}Mybatis Plus?:
public?class?CodeGenerator?{
????static?String?dbUrl?=?"jdbc:mysql://localhost:3306/fluent_Mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8";
????@Test
????public?void?generateCode()?{
????????GlobalConfig?config?=?new?GlobalConfig();
????????DataSourceConfig?dataSourceConfig?=?new?DataSourceConfig();
????????dataSourceConfig.setDbType(DbType.MYSQL)
????????????.setUrl(dbUrl)
????????????.setUsername("root")
????????????.setPassword("password")
????????????.setDriverName(Driver.class.getName());
????????StrategyConfig?strategyConfig?=?new?StrategyConfig();
????????strategyConfig
????????????.setCapitalMode(true)
????????????.setEntityLombokModel(false)
????????????.setNaming(NamingStrategy.underline_to_camel)
????????????.setColumnNaming(NamingStrategy.underline_to_camel)
????????????.setEntityTableFieldAnnotationEnable(true)
????????????.setFieldPrefix(new?String[]{"test_"})
????????????.setInclude(new?String[]{"student_score"})
????????????.setLogicDeleteFieldName("is_deleted")
????????????.setTableFillList(Arrays.asList(
????????????????new?TableFill("gmt_create",?FieldFill.INSERT),
????????????????new?TableFill("gmt_modified",?FieldFill.INSERT_UPDATE)));
????????config
????????????.setActiveRecord(false)
????????????.setIdType(IdType.AUTO)
????????????.setOutputDir(System.getProperty("user.dir")?+?"/src/main/java/")
????????????.setFileOverride(true);
????????new?AutoGenerator().setGlobalConfig(config)
????????????.setDataSource(dataSourceConfig)
????????????.setStrategy(strategyConfig)
????????????.setPackageInfo(
????????????????new?PackageConfig()
????????????????????.setParent("com.mp.demo")
????????????????????.setController("controller")
????????????????????.setEntity("entity")
????????????).execute();
????}
}三者對(duì)比總結(jié)
看完 3 個(gè)框架對(duì)同一個(gè)功能點(diǎn)的實(shí)現(xiàn), 各位看官肯定會(huì)有自己的判斷,筆者這里也總結(jié)了一份比較。
- Mybatis Plus Fluent Mybatis 代碼生成 生成? Entity生成? Entity, 再通過(guò)編譯生成?Mapper,?Query,?Update?和?SqlProviderGenerator 易用性 低 高 和 Mybatis 的共生關(guān)系 需替換原有的? SqlSessionFactoryBean對(duì) Mybatis 沒(méi)有任何修改,原來(lái)怎么用還是怎么用 動(dòng)態(tài) SQL 構(gòu)造方式 應(yīng)用啟動(dòng)時(shí), 根據(jù)? Entity?注解信息構(gòu)造動(dòng)態(tài) xml 片段,注入到 Mybatis 解析器應(yīng)用編譯時(shí),根據(jù)? Entity?注解,編譯生成對(duì)應(yīng)方法的?SqlProvider,利用 Mybatis 的?Mapper?上?@InsertProvider?、@SelectProvider?、@UpdateProvider?注解關(guān)聯(lián)動(dòng)態(tài) SQL 結(jié)果是否容易 DEBUG 跟蹤 不容易 debug 容易,直接定位到? SQLProvider?方法上,設(shè)置斷點(diǎn)即可動(dòng)態(tài) SQL 構(gòu)造 通過(guò)硬編碼字段名稱, 或者利用? Entity?的?get?方法的 lambda 表達(dá)式通過(guò)編譯手段生成對(duì)應(yīng)的方法名,直接調(diào)用方法即可 字段變更后的錯(cuò)誤發(fā)現(xiàn) 通過(guò)? get?方法的 lambda 表達(dá)的可以編譯發(fā)現(xiàn),通過(guò)字段編碼的無(wú)法編譯發(fā)現(xiàn)編譯時(shí)便可發(fā)現(xiàn) 不同字段動(dòng)態(tài) SQL 構(gòu)造方法 通過(guò)接口參數(shù)方式 通過(guò)接口名稱方式, Fluent API 的編碼效率更高 語(yǔ)法渲染特點(diǎn) 無(wú) 通過(guò)關(guān)鍵變量? select,?update,?set,?and,?or?可以利用 IDE 語(yǔ)法渲染, 可讀性更高Fluent Mybatis 實(shí)戰(zhàn)
接下來(lái),我們來(lái)看看如何使用 Fluent Mybatis 來(lái)實(shí)現(xiàn)增刪改查。
引入依賴
新建 Maven 工程,設(shè)置項(xiàng)目編譯級(jí)別為 Java8 及以上,引入 Fluent Mybatis 依賴包。
????
????
????????com.github.atool
????????fluent-mybatis
????????1.9.3
????
????
????
????????com.github.atool
????????fluent-mybatis-processor
????????1.9.3
????創(chuàng)建表
create?schema?fluent_mybatis;
create?table?hello_world
(
????id???????????bigint?unsigned?auto_increment?primary?key,
????say_hello????varchar(100)?null,
????your_name????varchar(100)?null,
????gmt_created???datetime???DEFAULT?NULL?COMMENT?'創(chuàng)建時(shí)間',
????gmt_modified?datetime???DEFAULT?NULL?COMMENT?'更新時(shí)間',
????is_deleted???tinyint(2)?DEFAULT?0?COMMENT?'是否邏輯刪除'
)?ENGINE?=?InnoDB
??CHARACTER?SET?=?utf8?comment?'簡(jiǎn)單演示表';創(chuàng)建數(shù)據(jù)庫(kù)表對(duì)應(yīng)的 Entity 類
創(chuàng)建數(shù)據(jù)庫(kù)表對(duì)應(yīng)的?
Entity?類:?HelloWorldEntity, 你只需要簡(jiǎn)單的做 3 個(gè)動(dòng)作:
根據(jù)駝峰命名規(guī)則命名? Entity?類和字段HelloWorldEntity?繼承?IEntity?接口類在? HelloWorldEntity?類上加注解?@FluentMybatis@FluentMybatis
public?class?HelloWorldEntity?extends?RichEntity?{
????private?Long?id;
????private?String?sayHello;
????private?String?yourName;
????private?Date?gmtCreated;
????private?Date?gmtModified;
????private?Boolean?isDeleted;
????//?get,?set,?toString?方法
???@Override
???public?Class?extends?IEntity>?entityClass()?{
??????return?HelloWorldEntity.class;
???}
}執(zhí)行編譯。
IDE 編譯:
Maven 編譯:
mvn clean compileGradle 編譯:
gradle clean compile配置數(shù)據(jù)源
數(shù)據(jù)源 DataSource 配置 Mybatis 的 mapper 掃描路徑 Mybatis 的? SqlSessionFactoryBean@ComponentScan(basePackages?=?"cn.org.atool.fluent.mybatis.demo1")
@MapperScan("cn.org.atool.fluent.mybatis.demo1.entity.mapper")
@Configuration
public?class?HelloWorldConfig?{
????/**
?????*?設(shè)置dataSource屬性
?????*
?????*?@return
?????*/
????@Bean
????public?DataSource?dataSource()?{
????????BasicDataSource?dataSource?=?new?BasicDataSource();
????????dataSource.setDriverClassName("com.mysql.jdbc.Driver");
????????dataSource.setUrl("jdbc:mysql://localhost:3306/fluent_mybatis?useUnicode=true&characterEncoding=utf8");
????????dataSource.setUsername("root");
????????dataSource.setPassword("password");
????????return?dataSource;
????}
????/**
?????*?定義mybatis的SqlSessionFactoryBean
?????*
?????*?@param?dataSource
?????*?@return
?????*/
????@Bean
????public?SqlSessionFactoryBean?sqlSessionFactoryBean(DataSource?dataSource)?{
????????SqlSessionFactoryBean?bean?=?new?SqlSessionFactoryBean();
????????bean.setDataSource(dataSource);
????????return?bean;
????}
???@Bean
???public?MapperFactory?mapperFactory()?{
??????return?new?MapperFactory();
???}
}很簡(jiǎn)單吧,在這里,你即不需要配置任何 Mybatis xml 文件, 也不需要寫任何 · 接口, 但你已經(jīng)擁有了強(qiáng)大的增刪改查的功能,并且是 Fluent API,讓我們寫一個(gè)測(cè)試來(lái)見證一下 Fluent Mybatis 的魔法力量!另外,搜索公眾號(hào)Linux就該這樣學(xué)后臺(tái)回復(fù)“git書籍”,獲取一份驚喜禮包。
測(cè)試
注入?
HelloWorldEntity?對(duì)應(yīng)的?Mapper?類:?HelloWorldMapper, 這個(gè)類是 Fluent Mybatis 編譯時(shí)生成的。使用?
HelloWorldMapper?進(jìn)行刪除、插入、查詢、修改操作。@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes?=?HelloWorldConfig.class)
public?class?HelloWorldTest?{
????/**
?????*?Fluent?Mybatis編譯時(shí)生成的Mapper類
?????*/
????@Autowired
????HelloWorldMapper?mapper;
????@Test
????public?void?testHelloWorld()?{
????????/**
?????????*?為了演示方便,先刪除數(shù)據(jù)
?????????*/
????????mapper.delete(mapper.query()
????????????.where.id().eq(1L).end());
????????/**
?????????*?插入數(shù)據(jù)
?????????*/
????????HelloWorldEntity?entity?=?new?HelloWorldEntity();
????????entity.setId(1L);
????????entity.setSayHello("hello?world");
????????entity.setYourName("Fluent?Mybatis");
????????entity.setIsDeleted(false);
????????mapper.insert(entity);
????????/**
?????????*?查詢?id?=?1?的數(shù)據(jù)
?????????*/
????????HelloWorldEntity?result1?=?mapper.findOne(mapper.query()
????????????.where.id().eq(1L).end());
????????/**
?????????*?控制臺(tái)直接打印出查詢結(jié)果
?????????*/
????????System.out.println("1.?HelloWorldEntity:"?+?result1.toString());
????????/**
?????????*?更新id?=?1的記錄
?????????*/
????????mapper.updateBy(mapper.updater()
????????????.set.sayHello().is("say?hello,?say?hello!")
????????????.set.yourName().is("Fluent?Mybatis?is?powerful!").end()
????????????.where.id().eq(1L).end()
????????);
????????/**
?????????*?查詢?id?=?1?的數(shù)據(jù)
?????????*/
????????HelloWorldEntity?result2?=?mapper.findOne(mapper.query()
????????????.where.sayHello().like("hello")
????????????.and.isDeleted().eq(false).end()
????????????.limit(1)
????????);
????????/**
?????????*?控制臺(tái)直接打印出查詢結(jié)果
?????????*/
????????System.out.println("2.?HelloWorldEntity:"?+?result2.toString());
????}
}輸出:
1.?HelloWorldEntity:HelloWorldEntity{id=1,?sayHello='hello?world',?yourName='Fluent?Mybatis',?gmtCreate=null,?gmtModified=null,?isDeleted=false}
2.?HelloWorldEntity:HelloWorldEntity{id=1,?sayHello='say?hello,?say?hello!',?yourName='Fluent?Mybatis?is?powerful!',?gmtCreate=null,?gmtModified=null,?isDeleted=false}神奇吧!我們?cè)俚綌?shù)據(jù)庫(kù)中查看一下結(jié)果
現(xiàn)在,我們已經(jīng)通過(guò)一個(gè)簡(jiǎn)單例子演示了 Fluent Mybatis 的強(qiáng)大功能, 在進(jìn)一步介紹 Fluent Mybatis 更強(qiáng)大功能前,我們揭示一下為啥我們只寫了一個(gè)數(shù)據(jù)表對(duì)應(yīng)的 Entity 類, 卻擁有了一系列增刪改查的數(shù)據(jù)庫(kù)操作方法。
Fluent Mybatis 根據(jù)?
Entity?類上?@FluentMybatis?注解在編譯時(shí), 會(huì)在 target 目錄 class 目錄下自動(dòng)編譯生成一系列文件:這些文件的具體作用如下:
mapper/*Mapper?: Mybatis 的?Mapper?定義接口, 定義了一系列通用的數(shù)據(jù)操作接口方法。dao/*BaseDao?:?Dao?實(shí)現(xiàn)基類, 所有的?DaoImpl?都繼承各自基類 根據(jù)分層編碼的原則,我們不會(huì)在?Service?類中直接使用?Mapper?類,而是引用?Dao?類。我們?cè)?Dao實(shí)現(xiàn)類中根據(jù)條件實(shí)現(xiàn)具體的數(shù)據(jù)操作方法。wrapper/*Query?: Fluent Mybatis 核心類, 用來(lái)進(jìn)行動(dòng)態(tài) sql 的構(gòu)造, 進(jìn)行條件查詢。wrapper/*Updater?: Fluent Mybatis 核心類, 用來(lái)動(dòng)態(tài)構(gòu)造?update?語(yǔ)句。helper/*Mapping?:?Entity?表字段和?Entity?屬性映射定義類helper/*Segment:?Query?和?Updater?具體功能實(shí)現(xiàn), 包含幾個(gè)實(shí)現(xiàn):?select,?where,?group by,?having by,?order by,?limitIEntityRelation?: 處理?Entity?關(guān)聯(lián)(一對(duì)一, 一對(duì)多, 多對(duì)多)關(guān)系的接口Ref?: 引用 Fluent Mybatis 生成的對(duì)象的快捷入口工具類總結(jié)
上面只是 Fluent Mybatis 常規(guī)實(shí)現(xiàn)增刪改查的方式,F(xiàn)luent Mybatis 現(xiàn)在又推出了專門面向表單級(jí)的增刪改查,聲明即實(shí)現(xiàn)。官方說(shuō)明:https://juejin.cn/post/7033388050012962852 。
PS:如果覺(jué)得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、轉(zhuǎn)發(fā)、在看。



Fluent Mybatis 特性
Fluent Mybatis 原理



