日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

一種避免寫(xiě)大量CRUD方法的新思路

來(lái)源: 責(zé)編: 時(shí)間:2024-04-30 08:42:50 278觀看
導(dǎo)讀哈嘍,各位代碼戰(zhàn)士們,我是Jensen,一個(gè)夢(mèng)想著和大家一起在代碼的海洋里遨游,順便撿起那些散落的知識(shí)點(diǎn)的程序員小伙伴。今天,我繼續(xù)給大家?guī)?lái)一個(gè)超級(jí)無(wú)敵霹靂的編碼新招式,來(lái)自我最近的親身實(shí)踐,我把公司的PHP工程(兩個(gè)端,幾

l0F28資訊網(wǎng)——每日最新資訊28at.com

哈嘍,各位代碼戰(zhàn)士們,我是Jensen,一個(gè)夢(mèng)想著和大家一起在代碼的海洋里遨游,順便撿起那些散落的知識(shí)點(diǎn)的程序員小伙伴。l0F28資訊網(wǎng)——每日最新資訊28at.com

今天,我繼續(xù)給大家?guī)?lái)一個(gè)超級(jí)無(wú)敵霹靂的編碼新招式,來(lái)自我最近的親身實(shí)踐,我把公司的PHP工程(兩個(gè)端,幾百個(gè)接口)重構(gòu)到Java工程上來(lái),僅僅用了兩天!l0F28資訊網(wǎng)——每日最新資訊28at.com

先看看業(yè)務(wù)——租賃平臺(tái)領(lǐng)域圖:l0F28資訊網(wǎng)——每日最新資訊28at.com

l0F28資訊網(wǎng)——每日最新資訊28at.com

乍一看這張領(lǐng)域圖就不簡(jiǎn)單(表梳理、核心業(yè)務(wù)梳理、建模等花了我兩天),順便用腳趾頭數(shù)了一下,總共是36張表,只談常規(guī)CRUD方法的話,要寫(xiě)36*4=144個(gè)API接口,這里還涉及客戶端和管理端API的隔離,那翻個(gè)倍就是288個(gè)API接口了唄。l0F28資訊網(wǎng)——每日最新資訊28at.com

CrudBoy是不可能的,這輩子都不可能的。l0F28資訊網(wǎng)——每日最新資訊28at.com

你信不信,我只寫(xiě)兩個(gè)Controller,就能把兩個(gè)端的CRUD全部搞定!l0F28資訊網(wǎng)——每日最新資訊28at.com

本文涉及技術(shù)點(diǎn):SpringMVC、MybatisPlusl0F28資訊網(wǎng)——每日最新資訊28at.com

一、思路分析

問(wèn)題來(lái)了,一個(gè)Controller怎么做到多張表的CRUD(增刪查改)呢?l0F28資訊網(wǎng)——每日最新資訊28at.com

要做到所有表共用一個(gè)Controller,就需要復(fù)用公共的CRUD方法。我們需要滿足以下5個(gè)條件:l0F28資訊網(wǎng)——每日最新資訊28at.com

  • 不同的表需要通過(guò)模型名稱進(jìn)行隔離
  • 通過(guò)模型名稱能找到對(duì)應(yīng)的模型類
  • 通過(guò)模型類能找到對(duì)應(yīng)的倉(cāng)庫(kù),從而操作數(shù)據(jù)庫(kù)
  • 對(duì)于查詢方法,請(qǐng)求參數(shù)能轉(zhuǎn)化為查詢條件,模型作為查詢返回類
  • 對(duì)于操作方法,請(qǐng)求參數(shù)能轉(zhuǎn)化為模型

只需要解決上述問(wèn)題,一個(gè)Controller即可解決所有表的CRUD需求。l0F28資訊網(wǎng)——每日最新資訊28at.com

老規(guī)矩——設(shè)計(jì)先行:l0F28資訊網(wǎng)——每日最新資訊28at.com

l0F28資訊網(wǎng)——每日最新資訊28at.com

好吧,我承認(rèn)這張圖是剛臨時(shí)畫(huà)的,代碼早就已經(jīng)實(shí)現(xiàn)了,正如你的產(chǎn)品經(jīng)理告訴你:l0F28資訊網(wǎng)——每日最新資訊28at.com

開(kāi)發(fā)小哥哥,客戶說(shuō)后天要上線這個(gè)新功能,能不能拜托你今天把這個(gè)小需求開(kāi)發(fā)完,晚上測(cè)試完就能發(fā)布上線了唄。l0F28資訊網(wǎng)——每日最新資訊28at.com

你不得不用腦子先畫(huà)個(gè)藍(lán)圖,邊寫(xiě)代碼邊小步迭代,做完后再補(bǔ)設(shè)計(jì)。l0F28資訊網(wǎng)——每日最新資訊28at.com

二、先造輪子

首先來(lái)個(gè)聚合控制器接口AggregateController:l0F28資訊網(wǎng)——每日最新資訊28at.com

/** * 聚合控制器,實(shí)現(xiàn)該控制器的Controller,自帶CRUD方法 * * @author Jensen * @公眾號(hào) 架構(gòu)師修行錄 */public interface AggregateController {    // 公共POST分頁(yè)    @PostMapping("/{modelName}/page")    default Page<Model> postPage(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {        return convertQuery(getModelClass(modelName), query).page();    }    // 公共GET分頁(yè)    @GetMapping("/{modelName}/page")    default Page<Model> getPage(@PathVariable("modelName") String modelName, Map<String, Object> query) {        return convertQuery(getModelClass(modelName), query).page();    }    // 公共POST列表    @PostMapping("/{modelName}/list")    default List<Model> postList(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {        return convertQuery(getModelClass(modelName), query).list();    }    // 公共GET列表    @GetMapping("/{modelName}/list")    default List<Model> getList(@PathVariable("modelName") String modelName, Map<String, Object> query) {        return convertQuery(getModelClass(modelName), query).list();    }    // 公共詳情,通過(guò)其他條件查第一條    @GetMapping("/{modelName}/detail")    default Model detail(@PathVariable("modelName") String modelName, Map<String, Object> query) {        return convertQuery(getModelClass(modelName), query).first();    }    // 公共詳情,通過(guò)ID查    @GetMapping("/{modelName}/detail/{id}")    default Model detail(@PathVariable("modelName") String modelName, @PathVariable("id") String id) {        return BaseRepository.of(getModelClass(modelName)).get(id);    }    // 公共創(chuàng)建    @PostMapping({"/{modelName}/save", "/{modelName}/create"})    default Model save(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {        Model model = convertModel(getModelClass(modelName), query);        model.save();        return model;    }    // 公共批量創(chuàng)建    @PostMapping("/{modelName}/saveBatch")    default void saveBatch(@PathVariable("modelName") String modelName, @RequestBody List<Map<String, Object>> params) {        Class<Model> modelClass = getModelClass(modelName);        BaseRepository.of(modelClass).save(convertModels(modelClass, params));    }    // 公共修改    @PostMapping({"/{modelName}/update", "/{modelName}/modify"})    default void update(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {        convertModel(getModelClass(modelName), query).update();    }    // 公共刪除    @PostMapping({"/{modelName}/delete/{id}", "/{modelName}/remove/{id}"})    default void delete(@PathVariable("modelName") String modelName, @PathVariable("id") String id) {        BaseRepository.of(getModelClass(modelName)).delete(id);    }    // 通過(guò)模型名找到模型類    static Class<Model> getModelClass(String modelName) {        Class<Model> modelClass = MappingKit.get("MODEL_NAME", modelName);        BizAssert.notNull(modelClass, "Model: {} not found", modelName);        return modelClass;    }    // 通過(guò)模型類找到查詢類,并把Map參數(shù)轉(zhuǎn)換為查詢參數(shù)    static Query convertQuery(Class<Model> modelClass, Map<String, Object> queryMap) {        Class<Query> queryClass = MappingKit.get("MODEL_QUERY", modelClass);        BizAssert.notNull(queryClass, "Query not found");        return BeanKit.ofMap(queryMap, queryClass);    }    // 通過(guò)Map參數(shù)轉(zhuǎn)換為模型    static Model convertModel(Class<Model> modelClass, Map<String, Object> modelMap) {        return BeanKit.ofMap(modelMap, modelClass);    }}

路徑參數(shù){modelName}就是模型名,比如建了個(gè)表user_info,對(duì)應(yīng)的模型是UserInfo,對(duì)應(yīng)的模型名叫userInfo。l0F28資訊網(wǎng)——每日最新資訊28at.com

下一步,我們需要通過(guò)這個(gè)動(dòng)態(tài)的模型名路由到對(duì)應(yīng)的模型上,怎么做呢?l0F28資訊網(wǎng)——每日最新資訊28at.com

這時(shí)候,我們需要在應(yīng)用啟動(dòng)時(shí),在初始化倉(cāng)庫(kù)實(shí)現(xiàn)類中獲取到模型后,注入到一個(gè)容器。l0F28資訊網(wǎng)——每日最新資訊28at.com

這里我們先定義一個(gè)基礎(chǔ)倉(cāng)庫(kù)接口:l0F28資訊網(wǎng)——每日最新資訊28at.com

/** * 基礎(chǔ)倉(cāng)庫(kù)接口 * 針對(duì)CRUD進(jìn)行封裝,業(yè)務(wù)倉(cāng)庫(kù)需要實(shí)現(xiàn)當(dāng)前接口 * * @author Jensen * @公眾號(hào) 架構(gòu)師修行錄 */public interface BaseRepository<M extends Model, Q extends Query> {    // 定義一個(gè)存放模型類/查詢類-倉(cāng)庫(kù)實(shí)現(xiàn)類映射的容器    Map<Class<?>, Class<?>> REPOSITORY_MAPPINGS = new ConcurrentHashMap<>();    /**     * 注入倉(cāng)庫(kù)類     *     * @param mappingClass    Model類/Query類     * @param repositoryClass 倉(cāng)庫(kù)類     */    static <R extends BaseRepository> void inject(Class<?> mappingClass, Class<R> repositoryClass) {        REPOSITORY_MAPPINGS.put(mappingClass, repositoryClass);    }    // TODO 封裝的CRUD方法暫且略過(guò)}

上面這種使用ConcurrentHashMap作為容器的技術(shù),在各個(gè)框架里隨處可見(jiàn),還是挺實(shí)用的,大家可以學(xué)一學(xué)。l0F28資訊網(wǎng)——每日最新資訊28at.com

接下來(lái),我們?cè)賹?duì)MybatisPlus的BaseMapper類進(jìn)行淺封裝,作為基礎(chǔ)倉(cāng)庫(kù)實(shí)現(xiàn)類,針對(duì)CRUD進(jìn)行二次封裝:l0F28資訊網(wǎng)——每日最新資訊28at.com

/** * 基礎(chǔ)倉(cāng)庫(kù)實(shí)現(xiàn)類 * 針對(duì)CRUD進(jìn)行封裝,業(yè)務(wù)倉(cāng)庫(kù)實(shí)現(xiàn)需要繼承當(dāng)前類 * * @author Jensen * @公眾號(hào) 架構(gòu)師修行錄 */public abstract class BaseRepositoryImpl<MP extends BaseMapper<P>, M extends Model, P, Q extends Query> implements BaseRepository<M, Q>, Serializable {    // 在倉(cāng)庫(kù)實(shí)現(xiàn)類構(gòu)造器中初始化各種映射信息    public BaseRepositoryImpl() {        // 通過(guò)反射工具,拿到具體的模型類        final Class<M> modelClass = (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), 1);        // 通過(guò)反射工具,拿到具體的持久化實(shí)體類        final Class<P> poClass = (Class<P>) ReflectionKit.getSuperClassGenericType(this.getClass(), 2);        // 通過(guò)反射工具,拿到具體的查詢類        final Class<Q> queryClass = (Class<Q>) ReflectionKit.getSuperClassGenericType(this.getClass(), 3);        // 注入模型類-倉(cāng)庫(kù)實(shí)現(xiàn)類        BaseRepository.inject(modelClass, this.getClass());        // 注入查詢類-倉(cāng)庫(kù)實(shí)現(xiàn)類        BaseRepository.inject(queryClass, this.getClass());        // 映射模型類-實(shí)體類        MappingKit.map("MODEL_PO", modelClass, poClass);        MappingKit.map("MODEL_PO", poClass, modelClass);       // 映射模型類-查詢類        MappingKit.map("MODEL_QUERY", modelClass, queryClass);        MappingKit.map("MODEL_QUERY", queryClass, modelClass);        // 映射模型名-模型類,模型名首字母設(shè)為小寫(xiě)(駝峰式命名)        String modelClassName = modelClass.getSimpleName().toLowerCase().substring(0, 1) + modelClass.getSimpleName().substring(1);        MappingKit.map("MODEL_NAME", modelClassName, modelClass);    }        // TODO 封裝的CRUD方法暫且略過(guò)}

上面的MappingKit是封裝好的用于Bean映射的容器工具類:l0F28資訊網(wǎng)——每日最新資訊28at.com

/** * 用于任意對(duì)象映射,按biz隔離(為了復(fù)用) */@UtilityClasspublic final class MappingKit {    // Bean容器    private final Map<String, Map<Object, Object>> BEAN_MAPPINGS = new ConcurrentHashMap<>();    public <K, V> void map(String biz, K key, V value) {        Map<Object, Object> mappings = BEAN_MAPPINGS.get(biz);        if (mappings == null) {            mappings = new ConcurrentHashMap<>();            BEAN_MAPPINGS.put(biz, mappings);        }        mappings.put(key, value);    }    public <K, V> V get(String field, K source) {        Map<Object, Object> mappings = BEAN_MAPPINGS.get(field);        if (mappings == null) return null;        return (V) mappings.get(source);    }}

注入的邏輯比較簡(jiǎn)單,就是建一個(gè)Map<String, Class>,key放模型名,Class放Model類,這樣就可以通過(guò)模型名找到對(duì)應(yīng)的Model類了。l0F28資訊網(wǎng)——每日最新資訊28at.com

在倉(cāng)庫(kù)實(shí)現(xiàn)類初始化時(shí),我們把其他必要信息先進(jìn)行映射,如通過(guò)Model類找RepositoryImpl倉(cāng)庫(kù)實(shí)現(xiàn)、通過(guò)Model類找Query類等等。l0F28資訊網(wǎng)——每日最新資訊28at.com

至此,我們把映射的工作完成了,大家可以回過(guò)頭看看AggregateController,就是實(shí)際通過(guò)模型名modelName從容器中取出模型類、模型類取出倉(cāng)庫(kù)的過(guò)程了。l0F28資訊網(wǎng)——每日最新資訊28at.com

還看不懂沒(méi)關(guān)系,結(jié)合上面的AC架構(gòu)圖,重新理解幾遍~l0F28資訊網(wǎng)——每日最新資訊28at.com

三、造完輪子,開(kāi)車!

打開(kāi)方式很簡(jiǎn)單,比如我按端隔離定義了下面兩個(gè)控制器,僅僅用幾行代碼就代替了288個(gè)API接口的編寫(xiě):l0F28資訊網(wǎng)——每日最新資訊28at.com

/** * 客戶端控制器 */@RestController@RequestMapping("/client")public class ClientController implements AggregateController {}
/** * 管理端控制器 */@RestController@RequestMapping("/admin")public class AdminController implements AggregateController {}

四、寫(xiě)在最后

希望今天分享的AC架構(gòu)能提高大家CRUD的效率,也讓系統(tǒng)重構(gòu)不再可怕。l0F28資訊網(wǎng)——每日最新資訊28at.com

對(duì)了,我把它的完整版集成到了我的D3Boot(DDD快速啟動(dòng))開(kāi)源基礎(chǔ)框架內(nèi),也有適用于部分場(chǎng)景使用的CRUDController,大家需要的話可以移步Gitee抄作業(yè)。l0F28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-86684-0.html一種避免寫(xiě)大量CRUD方法的新思路

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: Spring一個(gè)強(qiáng)大便捷的代理工廠類,你用過(guò)嗎?

下一篇: 深入剖析:如何使用Pulsar和Arthas高效排查消息隊(duì)列延遲問(wèn)題

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
国产精品毛片大码女人| 久久精品伊人| 在线观看日韩| 亚洲黄色成人久久久| 亚洲青色在线| 亚洲一区二区欧美| 久久国产精品一区二区| 老司机精品导航| 欧美日韩精品一区二区三区四区| 欧美午夜免费影院| 国产一区二区av| 亚洲国产精品专区久久| 99视频精品免费观看| 亚洲女同同性videoxma| 久久久久久久尹人综合网亚洲| 米奇777在线欧美播放| 欧美日韩午夜剧场| 国产欧美一级| 亚洲精品1区2区| 亚洲一区二区三区四区视频| 久久九九国产精品怡红院| 欧美激情亚洲精品| 国产日本欧美一区二区三区在线| 亚洲第一精品夜夜躁人人躁| 中日韩美女免费视频网址在线观看| 午夜精品久久久久久久| 美女主播精品视频一二三四| 国产精品成人免费| 1000部国产精品成人观看| 一区二区三区视频在线播放| 久久久久久久欧美精品| 欧美视频在线观看 亚洲欧| 狠狠88综合久久久久综合网| 99精品欧美一区二区三区综合在线| 欧美一区二区三区免费视| 欧美激情久久久| 国产有码一区二区| 亚洲色图制服丝袜| 美女诱惑黄网站一区| 国产欧美日韩一区二区三区| 日韩一级精品视频在线观看| 欧美在线看片| 欧美日韩一区二区在线视频| 在线看片欧美| 香蕉免费一区二区三区在线观看| 牛人盗摄一区二区三区视频| 国产精品一区二区黑丝| 亚洲精品久久嫩草网站秘色| 久久久久久亚洲综合影院红桃| 国产精品国产三级国产| 亚洲人成在线观看网站高清| 久久久久一区| 国产婷婷色一区二区三区在线 | 亚洲国产精品久久久久| 小嫩嫩精品导航| 欧美三级网页| 亚洲精品自在在线观看| 久久尤物视频| 国产在线精品成人一区二区三区 | 欧美黄色aa电影| 极品少妇一区二区三区| 校园春色国产精品| 国产精品免费区二区三区观看| 亚洲精品资源| 欧美黑人在线播放| 1204国产成人精品视频| 久久大香伊蕉在人线观看热2| 国产精品青草综合久久久久99 | 中文精品在线| 欧美女激情福利| 亚洲欧洲精品一区二区三区波多野1战4| 久久se精品一区二区| 国产精品一区二区久久| 亚洲少妇中出一区| 欧美日韩亚洲一区二区| 99精品99久久久久久宅男| 欧美激情一区在线观看| 亚洲激情图片小说视频| 欧美成人黑人xx视频免费观看| 在线观看av一区| 久热成人在线视频| 在线精品国产欧美| 久久综合九色综合久99| 激情欧美亚洲| 久久噜噜亚洲综合| 尤物yw午夜国产精品视频明星| 久久人人97超碰精品888| 激情伊人五月天久久综合| 久久久久久一区二区| 在线看片成人| 欧美大胆人体视频| 日韩视频精品| 欧美日韩在线一区二区| 在线亚洲美日韩| 国产精品免费一区二区三区在线观看| 亚洲性视频h| 国产啪精品视频| 久久久久久夜| 亚洲国产欧美一区| 欧美激情亚洲精品| 在线一区日本视频| 国产精品你懂的| 久久精品成人一区二区三区| 国产自产在线视频一区| 久久久久久一区二区| 在线视频国产日韩| 欧美日本一区| 亚洲欧美国产视频| 国产夜色精品一区二区av| 久久久久久久综合| 亚洲黄色av一区| 欧美日韩一区三区四区| 亚洲欧美经典视频| 国产在线视频欧美一区二区三区| 久久综合色8888| 亚洲精选在线| 国产精品萝li| 久久久www成人免费毛片麻豆| 亚洲福利国产| 欧美日韩午夜剧场| 欧美在线播放| 亚洲激情婷婷| 国产精品免费一区二区三区观看| 久久国产88| 亚洲精品国产视频| 国产精品视频一| 噜噜爱69成人精品| 亚洲视频在线观看三级| 国产亚洲视频在线| 欧美激情一区二区久久久| 亚洲免费中文| 在线观看一区欧美| 国产精品久久91| 久久婷婷国产综合国色天香 | 狂野欧美激情性xxxx欧美| 亚洲久色影视| 国产欧美日本一区视频| 免费日韩av| 亚洲在线免费观看| 一区二区视频免费在线观看| 欧美区在线播放| 欧美主播一区二区三区美女 久久精品人 | 亚洲男人影院| 亚洲第一色中文字幕| 国产精品video| 久久永久免费| 亚洲午夜三级在线| 亚洲大胆女人| 国产麻豆一精品一av一免费| 欧美大学生性色视频| 亚洲欧美日韩国产| 91久久极品少妇xxxxⅹ软件| 国产精品综合久久久| 欧美激情1区2区3区| 新狼窝色av性久久久久久| 亚洲精品久久在线| 亚洲线精品一区二区三区八戒| 黄色一区二区在线观看| 欧美午夜不卡影院在线观看完整版免费| 久久久久www| 亚洲伊人久久综合| 亚洲国产一区在线观看| 国产亚洲精品久久久久动| 欧美日韩免费视频| 久久亚洲春色中文字幕久久久| 亚洲综合视频网| 亚洲久久一区二区| 激情久久五月| 国产三级欧美三级日产三级99| 欧美日韩一区在线播放| 免费不卡在线视频| 久久国产精品久久精品国产| 亚洲一区二区毛片| 亚洲精品网站在线播放gif| 韩日精品视频一区| 国产精品一香蕉国产线看观看 | 欧美一区二区三区久久精品茉莉花| 亚洲免费观看视频| 亚洲国产黄色片| 国语自产偷拍精品视频偷| 国产精品久久二区| 国产精品video| 欧美三级日韩三级国产三级| 免费视频亚洲| 久久亚洲一区二区| 久久久7777| 久久精品国产免费观看| 亚洲欧美一区二区精品久久久| 艳女tv在线观看国产一区| 亚洲人成网站影音先锋播放| 亚洲电影观看| 在线视频国产日韩| 一区久久精品| 在线成人激情视频| 黑人一区二区三区四区五区| 国产一区二区你懂的| 国产欧美日韩| 国产日韩欧美视频| 国产日韩欧美中文在线播放| 国产精品视频一二三| 国产精品日韩精品欧美在线| 国产精品久久久久久久免费软件| 国产精品videossex久久发布| 欧美日韩系列|