關(guān)于Mybatis-Plus?Wrapper是否應(yīng)該出現(xiàn)在Servcie類中
一、問題
最近在做代碼重構(gòu),代碼工程采用了Controller/Service/Dao分層架構(gòu),Dao層使用了Mybatis-Plus框架。
在查看Service層時(shí)發(fā)現(xiàn)如下代碼:
@Service public class SampleServiceImpl implements SampleService { @Resource private SampleMapper sampleMapper; @Override public SampleTo findById(Long id) { Sample sample = this.sampleMapper.selectOne(Wrappers.<Sample>lambdaQuery() //僅查詢指定的column .select(Sample::getId, Sample::getName, Sample::getDate) //查詢條件 id = #{id} .eq(Sample::getId, id) ); return (SampleTo) BaseAssembler.populate(sample, new SampleTo()); } @Override public PageInfo<SampleTo> findListByDto(SampleQueryDto sampleQueryDto) { //開啟分頁 PageHelperUtil.startPage(sampleQueryDto); //查詢分頁列表 List<Sample> sampleList = this.sampleMapper.selectList(Wrappers.<Sample>lambdaQuery() //查詢條件 id = #{id} .eq(Objects.nonNull(sampleQueryDto.getId()), Sample::getId, sampleQueryDto.getId()) //查詢條件 name like concat('%', #{name}, '%') .like(StringUtils.hasText(sampleQueryDto.getName()), Sample::getName, sampleQueryDto.getName()) //查詢條件 type = #{type} .eq(StringUtils.hasText(sampleQueryDto.getType()), Sample::getType, sampleQueryDto.getType()) //查詢條件 date >= #{startDate} .ge(Objects.nonNull(sampleQueryDto.getStartDate()), Sample::getDate, sampleQueryDto.getStartDate()) //查詢條件 date <= #{endDate} .le(Objects.nonNull(sampleQueryDto.getEndDate()), Sample::getDate, sampleQueryDto.getEndDate())); //轉(zhuǎn)換分頁結(jié)果 return PageHelperUtil.convertPageInfo(sampleList, SampleTo.class); } @Override public List<SampleTo> findListByCondition(String name, String type) { List<Sample> sampleList = this.sampleMapper.selectList(Wrappers.<Sample>lambdaQuery() //查詢條件 name like concat('%', #{name}, '%') .like(StringUtils.hasText(name), Sample::getName, name) //查詢條件 type = #{type} .eq(StringUtils.hasText(type), Sample::getType, type) ); return BaseAssembler.populateList(sampleList, SampleTo.class); } @Override public SampleDetailTo findDetailById(Long id) { return this.sampleMapper.findDetail(id); } @Override public Integer add(SampleAddDto sampleAddDto) { Sample sample = new Sample(); BaseAssembler.populate(sampleAddDto, sample); return this.sampleMapper.insert(sample); } }
dao層代碼:
public interface SampleMapper extends BaseMapper<Sample> { //SQL腳本通過XML進(jìn)行定義 SampleDetailTo findDetail(@Param("id") Long id); }
如上Service代碼中直接使用Mybatis-Plus框架提供的Wrapper構(gòu)造器,寫的時(shí)候是挺爽,不用再單獨(dú)為SampleMapper接口寫XML腳本了,直接在Service類中都完成了,但是我不推薦這種寫法。
分層架構(gòu)的本意是通過分層來降低、隔離各層次的復(fù)雜度,
各層間僅通過接口進(jìn)行通信,層間僅依賴抽象接口,不依賴具體實(shí)現(xiàn),
只要保證接口不變可以自由切換每層的實(shí)現(xiàn)。
上述Service類中直接引用了Dao層實(shí)現(xiàn)框架Mybatis-Plus中的Wrappers類,盡管Servcie層依賴了Dao層的Mapper接口,但是Mapper接口中的參數(shù)Wrapper卻是Dao層具體實(shí)現(xiàn)Mybatis-Plus所獨(dú)有的,試想我們現(xiàn)在Dao層用的Mybatis-Plus實(shí)現(xiàn),后續(xù)如果想將Dao層實(shí)現(xiàn)切換為Spring JPA,那Mybatis-Plus中Wrapper是不都要替換,那Servcie層中的相關(guān)Wrapper引用也都要進(jìn)行替換,我們僅是想改變Dao實(shí)現(xiàn),卻不得不把Servcie層也進(jìn)行修改。同時(shí)Service層本該是寫業(yè)務(wù)邏輯代碼的地方,但是卻耦合進(jìn)了大量的Wrapper構(gòu)造邏輯,代碼可讀性差,難以捕捉到核心業(yè)務(wù)邏輯。
二、優(yōu)化建議
那是不是Mybatis-Plus中的Wrapper就不能用了呢?我的答案是:能用,只是方式?jīng)]用對。
Wrapper絕對是個(gè)好東西,方便我們構(gòu)造Sql,也可以將我們從繁瑣的XML腳本中解救出來,但是不能跨越層間界限。
優(yōu)化建議如下:
- 移除Servcie中的Wrapper使用
- Java8+之后接口提供了默認(rèn)方法的支持,可通過給Dao層Mapper接口添加default方法使用Wrapper
- 單表相關(guān)的操作 - 通過Dao層Mapper接口的default方法直接使用Wrapper進(jìn)行實(shí)現(xiàn),提高編碼效率
- 多表關(guān)聯(lián)的復(fù)雜操作 - 通過Dao層Mapper接口和XML腳本的方式實(shí)現(xiàn)
優(yōu)化后的Service層代碼如下:
@Service public class SampleServiceImpl implements SampleService { @Resource private SampleMapper sampleMapper; @Override public SampleTo findById(Long id) { Sample sample = this.sampleMapper.findInfoById(id); return (SampleTo) BaseAssembler.populate(sample, new SampleTo()); } @Override public SampleDetailTo findDetailById(Long id) { return this.sampleMapper.findDetail(id); } @Override public PageInfo<SampleTo> findListByDto(SampleQueryDto sampleQueryDto) { //開啟分頁 PageHelperUtil.startPage(sampleQueryDto); //查詢分頁列表 List<Sample> sampleList = this.sampleMapper.findList(sampleQueryDto); //轉(zhuǎn)換分頁結(jié)果 return PageHelperUtil.convertPageInfo(sampleList, SampleTo.class); } @Override public List<SampleTo> findListByCondition(String name, String type) { List<Sample> sampleList = this.sampleMapper.findListByNameAndType(name, type); return BaseAssembler.populateList(sampleList, SampleTo.class); } @Override public Integer add(SampleAddDto sampleAddDto) { Sample sample = new Sample(); BaseAssembler.populate(sampleAddDto, sample); return this.sampleMapper.insert(sample); } }
優(yōu)化后的Dao層代碼:
public interface SampleMapper extends BaseMapper<Sample> { default Sample findInfoById(Long id) { return this.selectOne(Wrappers.<Sample>lambdaQuery() //僅查詢指定的column .select(Sample::getId, Sample::getName, Sample::getDate) //查詢條件 id = #{id} .eq(Sample::getId, id) ); } default List<Sample> findList(SampleQueryDto sampleQueryDto) { return this.selectList(Wrappers.<Sample>lambdaQuery() //查詢條件 id = #{id} .eq(Objects.nonNull(sampleQueryDto.getId()), Sample::getId, sampleQueryDto.getId()) //查詢條件 name like concat('%', #{name}, '%') .like(StringUtils.hasText(sampleQueryDto.getName()), Sample::getName, sampleQueryDto.getName()) //查詢條件 type = #{type} .eq(StringUtils.hasText(sampleQueryDto.getType()), Sample::getType, sampleQueryDto.getType()) //查詢條件 date >= #{startDate} .ge(Objects.nonNull(sampleQueryDto.getStartDate()), Sample::getDate, sampleQueryDto.getStartDate()) //查詢條件 date <= #{endDate} .le(Objects.nonNull(sampleQueryDto.getEndDate()), Sample::getDate, sampleQueryDto.getEndDate()) ); } default List<Sample> findListByNameAndType(String name, String type) { return this.selectList(Wrappers.<Sample>lambdaQuery() //查詢條件 name like concat('%', #{name}, '%') .like(StringUtils.hasText(name), Sample::getName, name) //查詢條件 type = #{type} .eq(StringUtils.hasText(type), Sample::getType, type) ); } //SQL腳本通過XML進(jìn)行定義) SampleDetailTo findDetail(@Param("id") Long id); }
優(yōu)化后的Servcie層完全移除了對Wrapper的依賴,將Servcie層和Dao層實(shí)現(xiàn)進(jìn)行解耦,同時(shí)Dao層通過Java8+接口的默認(rèn)方法同時(shí)支持Wrapper和XML的使用,整合編碼和XML腳本的各自優(yōu)勢。
三、Repository模式
經(jīng)過優(yōu)化過后,Service層代碼確實(shí)清爽了許多,移除了Mybatis-Plus的Wrapper構(gòu)造邏輯,使得Service層可以更專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。但是細(xì)心的小伙伴還是會(huì)發(fā)現(xiàn)Servcie層仍舊依賴了Mybatis的分頁插件PageHelper中的PageHelper類、PageInfo類,PageHelper插件也是技術(shù)綁定的(強(qiáng)綁定到Mybatis),既然我們們之前強(qiáng)調(diào)了Servcie層與Dao層間的界限,如此在Servcie層使用PageHelper也是越界了,例如后續(xù)如果切換Spring JPA,那PageHelper在Servcie層的相關(guān)的引用也都需要調(diào)整。
真正做到業(yè)務(wù)和技術(shù)解耦,可以參考DDD中的Repository(倉庫、資源庫)模式:
- 單獨(dú)定義通用的分頁查詢參數(shù)DTO、分頁查詢結(jié)果DTO(與具體技術(shù)解耦)
- 定義Repository接口,僅依賴聚合、通用分頁查詢參數(shù)DTO、分頁查詢結(jié)果DTO
- 定義Repository接口的實(shí)現(xiàn)類,具體實(shí)現(xiàn)可依賴如Mybatis、JPA等Dao框架,在Repository的具體實(shí)現(xiàn)類中完成轉(zhuǎn)換:
- 領(lǐng)域模型(聚合)<==> 數(shù)據(jù)實(shí)體
- 通用分頁查詢參數(shù)DTO、結(jié)果DTO <==> Dao框架的分頁參數(shù)、結(jié)果(如PageHelper、IPage等)
DDD映射到代碼層面,改動(dòng)還是比較大的,所以在這次重構(gòu)代碼的過程中并沒有真正采用DDD Repository模式,
而是僅從Servcie中移除Mybatis-Plus Wrapper便結(jié)束了,雖沒有完全將Service層與Dao層實(shí)現(xiàn)(PageHelper)解耦,
但在Service層移除Wrapper構(gòu)造邏輯后,使得Service層代碼更清爽,可讀性更好了,重構(gòu)過程的代碼改動(dòng)量也在可接收的范圍內(nèi)。
到此這篇關(guān)于Mybatis-Plus Wrapper應(yīng)該出現(xiàn)在Servcie類中嗎?的文章就介紹到這了,更多相關(guān)Mybatis-Plus Wrapper內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mybatis-plus?Wrapper條件構(gòu)造器updateForSet更新方式
- MyBatis-Plus QueryWrapper及LambdaQueryWrapper的使用詳解
- mybatis-plus QueryWrapper 添加limit方式
- Mybatis-Plus 條件構(gòu)造器 QueryWrapper 的基本用法
- MyBatis-Plus實(shí)現(xiàn)2種分頁方法(QueryWrapper查詢分頁和SQL查詢分頁)
- mybatis-plus QueryWrapper自定義查詢條件的實(shí)現(xiàn)
- 詳解MyBatis-Plus Wrapper條件構(gòu)造器查詢大全
相關(guān)文章
如何利用JAVA正則表達(dá)式輕松替換JSON中的大字段
這篇文章主要給大家介紹了關(guān)于如何利用JAVA正則表達(dá)式輕松替換JSON中大字段的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java下3中XML解析 DOM方式、SAX方式和StAX方式
目前我知道的JAVA解析XML的方式有:DOM, SAX, StAX;如果選用這幾種,感覺還是有點(diǎn)麻煩;如果使用:JAXB(Java Architecture for XML Binding),個(gè)人覺得太方便了2013-04-04SpringCloud?服務(wù)注冊中的nacos實(shí)現(xiàn)過程
這篇文章主要介紹了SpringCloud?服務(wù)注冊之nacos實(shí)現(xiàn)過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03解決SpringBoot運(yùn)行Test時(shí)報(bào)錯(cuò):SpringBoot Unable to find
這篇文章主要介紹了SpringBoot運(yùn)行Test時(shí)報(bào)錯(cuò):SpringBoot Unable to find a @SpringBootConfiguration,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10spring boot項(xiàng)目沒有mainClass如何實(shí)現(xiàn)打包運(yùn)行
這篇文章主要介紹了spring boot項(xiàng)目沒有mainClass如何實(shí)現(xiàn)打包運(yùn)行,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Spring框架基于xml實(shí)現(xiàn)自動(dòng)裝配流程詳解
自動(dòng)裝配就是指?Spring?容器在不使用?<constructor-arg>?和<property>?標(biāo)簽的情況下,可以自動(dòng)裝配(autowire)相互協(xié)作的?Bean?之間的關(guān)聯(lián)關(guān)系,將一個(gè)?Bean?注入其他?Bean?的?Property?中2022-11-11如何基于回調(diào)實(shí)現(xiàn)Java的異步調(diào)用
這篇文章主要介紹了如何基于回調(diào)實(shí)現(xiàn)Java的異步調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06解決Java中的強(qiáng)制類型轉(zhuǎn)換和二進(jìn)制表示問題
這篇文章主要介紹了解決Java中的強(qiáng)制類型轉(zhuǎn)換和二進(jìn)制表示問題,需要的朋友可以參考下2019-05-05Knife4j?3.0.3?整合SpringBoot?2.6.4的詳細(xì)過程
本文要講的是?Knife4j?3.0.3?整合SpringBoot?2.6.4,在SpringBoot?2.4以上的版本和之前的版本還是不一樣的,這個(gè)也容易導(dǎo)致一些問題,本文就這兩個(gè)版本的整合做一個(gè)實(shí)戰(zhàn)介紹2022-09-09