前段時間我們介紹了如何使用thymeleaf去生成mybatis相關的模板:
https://www.lixiang.red/articles/2019/07/23/1563857782748.html
今天小刀和各位小夥伴們一起來深入下這個問題,我們來研究下怎麼去封裝一個通用的Mybatis模板,讓簡單的增刪改查操作,直接通過我們的代碼生成工具就能解決,讓工具從demo級別上升到生產可用的項目
相信很多小夥伴都已經用過Mybatis了,很簡單方便, 在springboot的集成環境裡面,寫個DAO接口,加上註解,然後寫個xml和DAO關聯起來,然後xml裡面方法和DAO裡面的方法關聯起來,然後就可以通過調用DAO方法的形式來調用這個sql,並獲得相應的返回結果,代碼結構如圖下所示:
這是以往的方式,在新版Mybatis中,我們有了一個新的選擇,用java類和註解的方式去完成一個sql ,官方文檔如下:
http://www.mybatis.org/mybatis-3/statement-builders.html
寫法如下:
只建議簡單的sql用註解的方式去處理,複雜的還是要手寫sql , 可以重新建一個DAO , 或者在provider裡面用字符串拼接的方式去完成
具體用法,大家可以參考上面的官方文檔中的地址
我們總說,我們一直都在做重複的增刪改查工作,現在我們就可以把這些基本的操作都封裝起來,把更多的精力放在應用高可用,高響應,業務邏輯的梳理上面
因此 ,我們的封裝也是以增刪改查四大方向為主.
在封裝之前,我們先約定以下規定:
1.資料庫對應的實體對象,我們用DO(domain object)來表示
2.資料庫對應的查詢對應,我們用QC(query object)來表示
3.各種操作方法的命名,如,getModel,listModels,saveModels等等
這樣把資料庫實體對象和查詢對象分開,因為我們的在加查詢條件的時候,比如通過idList去查詢,如果在DO裡面新加欄位就不太好,所以抽象了一個QC的概念,專門做資料庫查詢對象。
為什麼我們項目可以用模板生成工具進行生成呢,大家仔細研究下手中的項目可以發現我們的項目從controller開始,到最後的DAO其實都是有名字對應起來的,我們以查詢為例: listItems,通過這個名字我們可以直觀的看到,是獲取商品列表的.
Controller中的代碼:
@GetMapping("/item/query")@ResponseBodypublic BaseResponse<PageData<ItemDO>> queryItem(ItemDO item, Integer pageIndex , Integer pageSize){
List<ItemDO> items = itemManager.queryItem(item, pageIndex, pageSize);Long totalCount = itemManager.countItem(item);
pageIndex = pageIndex == null?1:pageIndex; pageSize = pageSize == null?20:pageSize;return BaseResponse.assemblePageResponse(items,totalCount,pageIndex,pageSize); }模板:
@GetMapping("/[(${table.javaCamelName})]/query") @ResponseBody public BaseResponse<PageData<[(${table.javaTableName})]DO>> query[(${table.javaTableName})]([(${table.javaTableName})]DO [(${table.javaCamelName})], Integer pageIndex , Integer pageSize){
List<[(${table.javaTableName})]DO> [(${table.javaCamelName})]s = [(${table.javaCamelName})]Manager.query[(${table.javaTableName})]([(${table.javaCamelName})], pageIndex, pageSize); Long totalCount = [(${table.javaCamelName})]Manager.count[(${table.javaTableName})]([(${table.javaCamelName})]);
pageIndex = pageIndex == null?1:pageIndex; pageSize = pageSize == null?20:pageSize; return BaseResponse.assemblePageResponse([(${table.javaCamelName})]s,totalCount,pageIndex,pageSize); }Manager中的代碼:
public List<ItemDO> queryItem(ItemDO item, int pageIndex , int pageSize);模板:
public List<[(${table.javaTableName})]DO> query[(${table.javaTableName})]([(${table.javaTableName})]DO [(${table.javaCamelName})], int pageIndex , int pageSize);ManagerImpl中的代碼:
@Overridepublic List<ItemDO> queryItem(ItemDO item, int pageIndex , int pageSize){ Page page = new Page(pageIndex,pageSize); ItemQC qc = new ItemQC(); qc.setPage(page); BeanUtils.copyProperties(item,qc); List<ItemDO> items = itemMapper.listItems(qc);return items; }模板:
@Override public List<[(${table.javaTableName})]DO> query[(${table.javaTableName})]([(${table.javaTableName})]DO [(${table.javaCamelName})], int pageIndex , int pageSize){ Page page = new Page(pageIndex,pageSize); [(${table.javaTableName})]QC qc = new [(${table.javaTableName})]QC(); qc.setPage(page); BeanUtils.copyProperties([(${table.javaCamelName})],qc); List<[(${table.javaTableName})]> [(${table.javaCamelName})]s = [(${table.javaCamelName})]Mapper.list[(${table.javaTableName})]s(qc);return [(${table.javaCamelName})]s; }Mapper中的代碼:
@SelectProvider(type = ItemProvider.class) List<ItemDO> listItems(ItemQC itemQC);模板:
@SelectProvider(type = [(${table.javaTableName})]Provider.class) List<[(${table.javaTableName})]DO> list[(${table.javaTableName})]s([(${table.javaTableName})]QC [(${table.javaCamelName})]QC);Provider中的代碼:
public String listItems(ItemQC itemQC){ SQL sql = new SQL() {{ SELECT(TABLE_FIELDS); FROM("item"); }}; MapperUtils.richWhereSql(sql, itemQC);
return sql.toString(); }模板:
public String list[(${table.javaTableName})]s([(${table.javaTableName})]QC [(${table.javaCamelName})]QC){ SQL sql = new SQL() {{ SELECT(TABLE_FIELDS); FROM("[(${table.javaCamelName})]"); }}; MapperUtils.richWhereSql(sql, [(${table.javaCamelName})]QC);
return sql.toString(); }這樣我們從前到後看過來,發其實都是圍繞著一個關鍵詞在走: Item , 有時候把他變成大寫的,有時候把他變成駝峰命名,有時候把他加個DO後綴,有時候把他加個Mapper後綴.
生成文件到對應目錄中在上篇文章中,我們只是生成了對應的字符串,但是在實際開發中,我們是需要生成文件的,最好是能在對應文件夾中.
生成對應文件
所以這就有兩步,1是生成文件.2是找到對應文件夾生成文件這個應該是很簡單的,直接一句代碼搞定:
Files.write(Paths.get(pathMap.get("model")+tableInfo.getJavaTableName() + "DO.java"),model.getBytes());Files.write(Paths.get(pathMap.get("mapper")+tableInfo.getJavaTableName() + "Mapper.java"),dao.getBytes());Files.write(Paths.get(pathMap.get("provider")+tableInfo.getJavaTableName() + "Provider.java"),provider.getBytes());Files.write(Paths.get(pathMap.get("manager")+tableInfo.getJavaTableName() + "Manager.java"),manager.getBytes());Files.write(Paths.get(pathMap.get("managerImpl")+tableInfo.getJavaTableName() + "ManagerImpl.java"),managerImpl.getBytes()); Files.write(Paths.get(pathMap.get("controller")+tableInfo.getJavaTableName() + "Controller.java"),controller.getBytes());輸出到對應文件夾中這一步,就要我們明確,我們的每個文件是放在什麼地方在,如Mapper/Provider是查詢資料庫用的, 我們把他放在business-impl模塊中的dao文件夾中,下面小刀把自己項目中放的位置列出來和大家分享下,大家有好的想法可以一起交流:
XXXDO放在 business 模塊中 models.dos文件夾中
XXXController 放在admin 模塊中 controller 文件夾中
XXXManager 放在business模塊中 business文件夾中
XXXManagerImpl 放在business-impl模塊中 business.impl文件夾中
XXXMapper/XXXProvider 放在 business-impl模塊中 dao 文件夾中
這樣我們就可以用代碼去定位到對應的文件夾中了:// 獲得當前項目的文件夾String currentDir = System.getProperty("user.dir"); currentDir=currentDir.substring(0,currentDir.lastIndexOf("/")); pathMap.put("model",currentDir); pathMap.put("mapper",currentDir); pathMap.put("provider",currentDir); pathMap.put("manager",currentDir); pathMap.put("managerImpl",currentDir); pathMap.put("controller",currentDir);//如果設置了自動路徑,就YY一下路徑if(autoFilePath){ Files.list(Paths.get(currentDir)).forEach(file->{if(Files.isDirectory(file)){String fileName = file.toString();
if(fileName.endsWith("admin")){ pathMap.put("controller",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/admin/controller/"); }if(fileName.endsWith("business")){ pathMap.put("model",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/models/dos/"); pathMap.put("manager",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/business/manager/"); }if(fileName.endsWith("business-impl")){ pathMap.put("managerImpl",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/business/manager/impl/"+domainName+"/"); pathMap.put("mapper",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/dao/"+domainName+"/"); pathMap.put("provider",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/dao/"+domainName+"/"); } }
}); }這樣我們就可以做到,把增刪改查從controller到dao直接一鍵生成到對應文件夾啦,只要把表建好,剩下的事就一步搞定啦
最後說兩句這個工具一般是做後臺管理頁面,增刪改查的時候用, 業務邏輯不建議用工具類生成,業務邏輯一般是提供dubbo接口出來,不要直接把manager中的增刪改查提供出去,提供出去的一定是要先有業務,再有接口,然後在serviceImpl中調用manager去完成業務邏輯
大家有什麼想法,歡迎留言或者加小刀微信: best396975802