成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一種避免寫大量CRUD方法的新思路

開發 架構
今天,我繼續給大家帶來一個超級無敵霹靂的編碼新招式,來自我最近的親身實踐,我把公司的PHP工程(兩個端,幾百個接口)重構到Java工程上來,僅僅用了兩天!

哈嘍,各位代碼戰士們,我是Jensen,一個夢想著和大家一起在代碼的海洋里遨游,順便撿起那些散落的知識點的程序員小伙伴。

今天,我繼續給大家帶來一個超級無敵霹靂的編碼新招式,來自我最近的親身實踐,我把公司的PHP工程(兩個端,幾百個接口)重構到Java工程上來,僅僅用了兩天!

先看看業務——租賃平臺領域圖:

乍一看這張領域圖就不簡單(表梳理、核心業務梳理、建模等花了我兩天),順便用腳趾頭數了一下,總共是36張表,只談常規CRUD方法的話,要寫36*4=144個API接口,這里還涉及客戶端和管理端API的隔離,那翻個倍就是288個API接口了唄。

CrudBoy是不可能的,這輩子都不可能的。

你信不信,我只寫兩個Controller,就能把兩個端的CRUD全部搞定!

本文涉及技術點:SpringMVC、MybatisPlus

一、思路分析

問題來了,一個Controller怎么做到多張表的CRUD(增刪查改)呢?

要做到所有表共用一個Controller,就需要復用公共的CRUD方法。我們需要滿足以下5個條件:

  • 不同的表需要通過模型名稱進行隔離
  • 通過模型名稱能找到對應的模型類
  • 通過模型類能找到對應的倉庫,從而操作數據庫
  • 對于查詢方法,請求參數能轉化為查詢條件,模型作為查詢返回類
  • 對于操作方法,請求參數能轉化為模型

只需要解決上述問題,一個Controller即可解決所有表的CRUD需求。

老規矩——設計先行:

好吧,我承認這張圖是剛臨時畫的,代碼早就已經實現了,正如你的產品經理告訴你:

開發小哥哥,客戶說后天要上線這個新功能,能不能拜托你今天把這個小需求開發完,晚上測試完就能發布上線了唄。

你不得不用腦子先畫個藍圖,邊寫代碼邊小步迭代,做完后再補設計。

二、先造輪子

首先來個聚合控制器接口AggregateController:

/**
 * 聚合控制器,實現該控制器的Controller,自帶CRUD方法
 *
 * @author Jensen
 * @公眾號 架構師修行錄
 */
public interface AggregateController {
    // 公共POST分頁
    @PostMapping("/{modelName}/page")
    default Page<Model> postPage(@PathVariable("modelName") String modelName, @RequestBody Map<String, Object> query) {
        return convertQuery(getModelClass(modelName), query).page();
    }
    // 公共GET分頁
    @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();
    }
    // 公共詳情,通過其他條件查第一條
    @GetMapping("/{modelName}/detail")
    default Model detail(@PathVariable("modelName") String modelName, Map<String, Object> query) {
        return convertQuery(getModelClass(modelName), query).first();
    }
    // 公共詳情,通過ID查
    @GetMapping("/{modelName}/detail/{id}")
    default Model detail(@PathVariable("modelName") String modelName, @PathVariable("id") String id) {
        return BaseRepository.of(getModelClass(modelName)).get(id);
    }
    // 公共創建
    @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;
    }
    // 公共批量創建
    @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);
    }
    // 通過模型名找到模型類
    static Class<Model> getModelClass(String modelName) {
        Class<Model> modelClass = MappingKit.get("MODEL_NAME", modelName);
        BizAssert.notNull(modelClass, "Model: {} not found", modelName);
        return modelClass;
    }
    // 通過模型類找到查詢類,并把Map參數轉換為查詢參數
    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);
    }
    // 通過Map參數轉換為模型
    static Model convertModel(Class<Model> modelClass, Map<String, Object> modelMap) {
        return BeanKit.ofMap(modelMap, modelClass);
    }
}

路徑參數{modelName}就是模型名,比如建了個表user_info,對應的模型是UserInfo,對應的模型名叫userInfo。

下一步,我們需要通過這個動態的模型名路由到對應的模型上,怎么做呢?

這時候,我們需要在應用啟動時,在初始化倉庫實現類中獲取到模型后,注入到一個容器。

這里我們先定義一個基礎倉庫接口:

/**
 * 基礎倉庫接口
 * 針對CRUD進行封裝,業務倉庫需要實現當前接口
 *
 * @author Jensen
 * @公眾號 架構師修行錄
 */
public interface BaseRepository<M extends Model, Q extends Query> {
    // 定義一個存放模型類/查詢類-倉庫實現類映射的容器
    Map<Class<?>, Class<?>> REPOSITORY_MAPPINGS = new ConcurrentHashMap<>();
    /**
     * 注入倉庫類
     *
     * @param mappingClass    Model類/Query類
     * @param repositoryClass 倉庫類
     */
    static <R extends BaseRepository> void inject(Class<?> mappingClass, Class<R> repositoryClass) {
        REPOSITORY_MAPPINGS.put(mappingClass, repositoryClass);
    }
    // TODO 封裝的CRUD方法暫且略過
}

上面這種使用ConcurrentHashMap作為容器的技術,在各個框架里隨處可見,還是挺實用的,大家可以學一學。

接下來,我們再對MybatisPlus的BaseMapper類進行淺封裝,作為基礎倉庫實現類,針對CRUD進行二次封裝:

/**
 * 基礎倉庫實現類
 * 針對CRUD進行封裝,業務倉庫實現需要繼承當前類
 *
 * @author Jensen
 * @公眾號 架構師修行錄
 */
public abstract class BaseRepositoryImpl<MP extends BaseMapper<P>, M extends Model, P, Q extends Query> implements BaseRepository<M, Q>, Serializable {


    // 在倉庫實現類構造器中初始化各種映射信息
    public BaseRepositoryImpl() {
        // 通過反射工具,拿到具體的模型類
        final Class<M> modelClass = (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), 1);
        // 通過反射工具,拿到具體的持久化實體類
        final Class<P> poClass = (Class<P>) ReflectionKit.getSuperClassGenericType(this.getClass(), 2);
        // 通過反射工具,拿到具體的查詢類
        final Class<Q> queryClass = (Class<Q>) ReflectionKit.getSuperClassGenericType(this.getClass(), 3);
        // 注入模型類-倉庫實現類
        BaseRepository.inject(modelClass, this.getClass());
        // 注入查詢類-倉庫實現類
        BaseRepository.inject(queryClass, this.getClass());
        // 映射模型類-實體類
        MappingKit.map("MODEL_PO", modelClass, poClass);
        MappingKit.map("MODEL_PO", poClass, modelClass);
       // 映射模型類-查詢類
        MappingKit.map("MODEL_QUERY", modelClass, queryClass);
        MappingKit.map("MODEL_QUERY", queryClass, modelClass);
        // 映射模型名-模型類,模型名首字母設為小寫(駝峰式命名)
        String modelClassName = modelClass.getSimpleName().toLowerCase().substring(0, 1) + modelClass.getSimpleName().substring(1);
        MappingKit.map("MODEL_NAME", modelClassName, modelClass);
    }
    
    // TODO 封裝的CRUD方法暫且略過
}

上面的MappingKit是封裝好的用于Bean映射的容器工具類:

/**
 * 用于任意對象映射,按biz隔離(為了復用)
 */
@UtilityClass
public 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);
    }
}

注入的邏輯比較簡單,就是建一個Map<String, Class>,key放模型名,Class放Model類,這樣就可以通過模型名找到對應的Model類了。

在倉庫實現類初始化時,我們把其他必要信息先進行映射,如通過Model類找RepositoryImpl倉庫實現、通過Model類找Query類等等。

至此,我們把映射的工作完成了,大家可以回過頭看看AggregateController,就是實際通過模型名modelName從容器中取出模型類、模型類取出倉庫的過程了。

還看不懂沒關系,結合上面的AC架構圖,重新理解幾遍~

三、造完輪子,開車!

打開方式很簡單,比如我按端隔離定義了下面兩個控制器,僅僅用幾行代碼就代替了288個API接口的編寫:

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

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


}

四、寫在最后

希望今天分享的AC架構能提高大家CRUD的效率,也讓系統重構不再可怕。

對了,我把它的完整版集成到了我的D3Boot(DDD快速啟動)開源基礎框架內,也有適用于部分場景使用的CRUDController,大家需要的話可以移步Gitee抄作業。

責任編輯:姜華 來源: 架構師修行錄
相關推薦

2024-04-26 08:58:54

if-else代碼JavaSpring

2024-05-09 08:20:29

AC架構數據庫冗余存儲

2022-06-23 07:05:46

跳板機服務器PAM

2018-04-18 07:34:58

2016-10-26 09:12:58

2023-09-17 23:16:46

緩存數據庫

2025-01-27 13:00:00

2020-11-27 14:45:57

開發服務器代碼

2016-10-13 10:57:55

phptcp專欄

2017-08-24 15:02:01

前端增量式更新

2019-11-22 09:21:17

技術研發數據

2021-05-18 06:22:39

CSS 制作波浪技巧

2017-01-23 11:18:16

戴爾

2009-12-03 10:32:21

2018-12-14 14:30:12

安全檢測布式系測試

2011-07-04 17:53:48

快速測試

2010-03-26 13:34:47

CentOS安裝

2021-06-11 00:11:23

GPS數據協議

2015-05-07 14:24:36

everRun

2018-02-08 08:11:41

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 色视频免费 | 久久久久久亚洲精品 | 国产精品日日做人人爱 | 国产精品二区三区 | 国产精品影视在线观看 | 无吗视频 | 国产精品成人久久久久 | 国产三级电影网站 | 天天干天天草 | 国产精品久久久久久妇女 | 中文字幕在线观看一区 | 久久久亚洲| 国产美女网站 | 北条麻妃视频在线观看 | 精品国产精品三级精品av网址 | 2018天天干天天操 | 精品福利一区二区三区 | 97精品超碰一区二区三区 | 99久久久久国产精品免费 | 91亚洲国产成人精品一区二三 | 伊人久久大香线 | 亚洲欧美成人 | 成人做爰www免费看视频网站 | 久久精品国产一区老色匹 | www.亚洲 | 亚洲www| 免费激情 | 热久久久| 91在线精品一区二区 | 中文字幕免费中文 | 国产精品久久国产精品 | 国产欧美日韩一区二区三区在线 | 天天操天天射综合 | 亚洲女人天堂成人av在线 | a级在线免费 | 久久综合久久综合久久 | 99精品国自产在线 | 日日草夜夜草 | 国产日韩久久 | 日本在线中文 | 亚洲最大成人综合 |