脈脈火爆全網(wǎng)的開源基于spring-jdbc生態(tài)的數(shù)據(jù)庫操作工具(脈脈數(shù)據(jù)研究院)
簡介
AnyLine的核心是一個基于spring-jdbc生態(tài)的(No-ORM)數(shù)據(jù)庫操作工具
其重點是:
1.以最簡單、快速的方式操作數(shù)據(jù)庫
2.針對結(jié)果集的數(shù)據(jù)二次處理能力
摒棄了各種繁瑣呆板的Service/Dao/Entity/*O/Mapper 沒有mybatis 沒有各種配置 各種O
適用場景
Anyline一的切都是面向動態(tài)、面向運行時環(huán)境
適合于抽象設(shè)計階段(實體概念還不明確或者設(shè)計不限于某個特別的實體)
常用于需要大量復(fù)雜動態(tài)的查詢,以及查詢的結(jié)果集需要經(jīng)過深度處理的場景 如:
- 可視化數(shù)據(jù)源
- 低代碼后臺
- 物聯(lián)網(wǎng)數(shù)據(jù)處理
- 數(shù)據(jù)清洗、數(shù)據(jù)批量處理
- 報表輸出,特別是自定義報表
- 運行時自定義表單/查詢條件/數(shù)據(jù)結(jié)構(gòu)
- 還有一種很實現(xiàn)的場景是 許多項目到了交付的那一天 實體也沒有設(shè)計完成
不適用場景
對已經(jīng)非常明確的實體執(zhí)行增刪改查操作
不要跨過設(shè)計人員直接拿給業(yè)務(wù)開發(fā)人員用
如何使用
數(shù)據(jù)操作不要再從生成xml/dao/service以及各種配置各種O開始
默認的service已經(jīng)提供了大部分的數(shù)據(jù)庫操作功能。
操作過程大致如下:
DataSet set = service.querys("HR_USER(ID,NM)", condition(true,"anyline根據(jù)約定自動生成的=,in,like等查詢條件"));
這里的查詢條件不再需要各種配置,各種if else foreach標(biāo)簽
Anyline會自動生成,生成規(guī)則可以參考這里的【約定規(guī)則】
分頁也不需要另外的插件,更不需要繁瑣的計算和配置,指定true或false即可
繁瑣機械的工作不要浪費程序員的時間
返回的DataSet上附加了常用的數(shù)據(jù)二次處理功能如:排序、維度轉(zhuǎn)換、截取、去重、方差、偏差、交集合集差集、分組、
行列轉(zhuǎn)換、類SQL過濾篩選(like,eq,in,less,between…)、JSON、XML格式轉(zhuǎn)換等
如何集成
只需要一個依賴、一個注解即可實現(xiàn)與springboot,netty等框架項目完美整合
直接看代碼【anyline-simple-hello】
生產(chǎn)環(huán)境可以參考這幾個pom
【anyboot-start】 沒有web環(huán)境,如定時任務(wù),爬蟲等
【anyboot-start-mvc】 基于spring-mvc
【anyboot-start-mvc-mysql】 基于spring-mvc MySQL數(shù)據(jù)庫
【anyboot-start-mvc-jsp-mysql】 基于spring-mvc MySQL數(shù)據(jù)庫 支持JSP
以下可以略過
根據(jù)數(shù)據(jù)庫類型添加依賴,如
<dependency> <groupId>org.anyline</groupId> <artifactId>anyline-jdbc-mysql(oracle|clickhouse...)</artifactId> <version>8.5.3-20220630</version></dependency>
在需要操作數(shù)據(jù)庫的地方注入AnylineService
@Qualifier("anyline.service")protected AnylineService service;
接下來service就可以完成大部分的數(shù)據(jù)庫操作了。常用示例可以參考【示例代碼】
兼容
如果實現(xiàn)放不下那些已存在的各種XOOO
DataSet與Entity之間可以相互轉(zhuǎn)換
或者這樣:
EntitySet<User> = service.querys(User.class, condition(true,"anyline根據(jù)約定自動生成的查詢條件")); //true:表示需要分頁//為什么不用返回的是一個EntitySet而不是List?//因為分頁情況下,EntitySet中包含了分頁數(shù)據(jù),而List不行。//無論是否分頁都返回相同的數(shù)據(jù)結(jié)構(gòu),而不需要根據(jù)是否分頁實現(xiàn)兩個接口返回不同的數(shù)據(jù)結(jié)構(gòu)//也可以這樣(如果真要這樣就不要用anyline了,還是用MyBatis,Hibernate之類吧)public class UserService extends AnylinseService<User> userService.querys(condition(true,"anyline根據(jù)約定自動生成的查詢條件"));
實戰(zhàn)對比
在理想的HelloWord環(huán)境下,任何方式都可以快速實現(xiàn)目標(biāo),更能體現(xiàn)優(yōu)劣的是復(fù)雜多變的實戰(zhàn)環(huán)境。
首先要承認銀彈是沒有的,所以先說 劣勢
- 在增、刪、改、查4個過程中,增的環(huán)境劣勢比較明顯
- 操作查詢結(jié)果時,不能像Entity一樣有IDE的提示和自動補齊,減少了IDE的協(xié)助確實讓許多人寸步難行,
大部分人也是在這里被勸退的。 - 在插入數(shù)據(jù)時,不能像像Entity一樣:userService.save(user),而是需要指定表名:service.save(HR_USER, row);
以上問題如果平衡的
- AnyLine返回的結(jié)果集與Entity之間隨時可以相互轉(zhuǎn)換,也可以在查詢時直接返回Entity
有思想的程序員會想為何要造個輪子 可靠嗎,所以再說 疑問
- AnylineLine并非新造了一個輪子,只是簡單的把業(yè)務(wù)參數(shù)傳給了底層的spring-jdbc
接下來的操作(如事務(wù)控制、連接池等)完全交給了spring-jdbc(沒有能力作好的事我們不作) - 如果非要說是一個新輪子,那只能說原來的輪子太難用,太消耗程序員體力了。
正事還沒開始就先生成一堆的mapper,OOO,各種鋪墊
鋪墊完了要操作數(shù)據(jù)實現(xiàn)業(yè)務(wù)了,依然啰嗦,各種 勞力 不勞心 的遍歷及加減乘除
所以重點說 優(yōu)勢
1.關(guān)于查詢條件
這是開發(fā)人員最繁重的體力勞動之一
接收參數(shù)、驗證、格式化、層層封裝傳遞到mapper.xml,再各種判斷、遍歷就為生成一條SQL
下面的這些標(biāo)簽許多人可能感覺習(xí)以為常了
<if test="code != null and code != '' "> AND CODE = #{code} </if> <if test="name != null and name != '' "> AND NAME like concat('%',#{name},'%')</if><if test="types != null and types.size > 0 "> AND TYPE IN <foreach collection="types" item="type" open="(" close=")" separator=","> #{type} </foreach></if>
但這并不正常,這期間還有什么是必須程序員參的,程序員不參與就自動不了,就約定不了的嗎? 換一種方式處理: 不要mapper.xml了,也更不要定位SQL的ID的 直接在java中這樣處理,其他的交給工具 condition("CODE:code","NAME:name%", "TYPE:[type]")
這應(yīng)該不需要注釋了,更多的約定可以參考這里的【約定規(guī)則】
2.結(jié)果集的二次操作
這是開發(fā)人員最繁重的勞動之二
從數(shù)據(jù)庫中查詢出數(shù)據(jù)后,根據(jù)業(yè)務(wù)需求還需要對結(jié)果集作各種操作,最簡單的如加減乘除、交集差集、篩選過濾等
這些常見的操作DataSet中都已經(jīng)提供默認實現(xiàn)了,如ngl表達式、聚合函數(shù)、類SQL篩選過濾、維度轉(zhuǎn)換等。
3.關(guān)于面向動態(tài)與運行時環(huán)境
這里說的動態(tài)是指出動態(tài)數(shù)據(jù)源、動態(tài)數(shù)據(jù)結(jié)構(gòu)、動態(tài)結(jié)果集
運行時環(huán)境是指在系統(tǒng)運行階段才能確定以上內(nèi)容,而不是在需求、設(shè)計、編碼階段
動態(tài)數(shù)據(jù)源:
一般是在系統(tǒng)運行時生成
典型場景如數(shù)據(jù)中臺,用戶通過管理端提交第三方數(shù)據(jù)庫的地址帳號,中臺匯聚多個數(shù)據(jù)源的數(shù)據(jù)
這種情況下顯示不是在配置文件中添加多個數(shù)據(jù)源可以解決的
而是需要在接收到用戶提交數(shù)據(jù)后,生成動態(tài)的數(shù)據(jù)源
生成的動態(tài)數(shù)據(jù)源最好交給Spring等容器管理
以充分利用其生態(tài)內(nèi)的連接池,事務(wù)管理,切面等現(xiàn)有工具
在切換數(shù)據(jù)源時也不能通過切面來實現(xiàn)
而是根據(jù)組織或租戶身份等上下文環(huán)境來切換
動態(tài)數(shù)據(jù)結(jié)構(gòu):
一般由非專業(yè)開發(fā)人員甚至是最終用戶來設(shè)計表結(jié)構(gòu)
根據(jù)用戶設(shè)置或不同場景返回不同結(jié)構(gòu)的結(jié)果集
查詢條件也由用戶動態(tài)指定
結(jié)果集與查詢條件的選擇范圍也不能在編碼階段設(shè)置限定
典型場景如物聯(lián)網(wǎng)平臺儀器設(shè)備參數(shù)、低代碼平臺、報表工具
常用的數(shù)據(jù)結(jié)構(gòu)有兩種
1).DataRow類似于一個Map
2).DataSet是DataRow的集合,并內(nèi)含了分頁信息
以下場景中將逐步體現(xiàn)出相對于List,Entity的優(yōu)勢
1). 最常見的如更新或查詢部分列
DataRow row = service.query("HR_USER(ID,CODE)")
service.update(row,"CODE")
2).可視化數(shù)據(jù)源、報表輸出、數(shù)據(jù)清洗
這些場景下都需要的數(shù)據(jù)結(jié)構(gòu)都是靈活多變的
經(jīng)常是針對不同的業(yè)務(wù)從多個表中合成不同的結(jié)構(gòu)集
甚至是運行時根據(jù)用戶輸入動態(tài)結(jié)合的結(jié)構(gòu)集
輸出結(jié)果集后又需要大量的對比及聚合操作
這種情況下顯示不可能為每個結(jié)果集生成一個對應(yīng)Entity,只能是動態(tài)的Map結(jié)構(gòu)
在對結(jié)構(gòu)集的二次操作上,DataRow/DataSet可以在抽象設(shè)計階段就完成,而Entity卻很難
3).低代碼后臺、元數(shù)據(jù)管理
作為一個低代碼的后臺,首先需要具體靈活可定制的表結(jié)構(gòu)(通常會是一個半靜半動的結(jié)構(gòu))
我們將不再操作具體的業(yè)務(wù)對象與屬性。對大部分業(yè)務(wù)的操作都只能通過抽象的元數(shù)據(jù)進行。
舉例來說一個簡單的求和過程,原來在對靜態(tài)結(jié)構(gòu)時常用的的遍歷、Lamda、反射都難堪重任了。
我們能接收到的信息通常是這樣的:類型(學(xué)生)、屬性(年齡)、條件(年級=1)、聚合公式(平均值)
Anyline的實現(xiàn)過程類似這樣
DataSet set = service.querys(學(xué)生,年級=1);
int 平均年齡 = set.agg(平均值,年齡);
4).運行時自定義表單、查詢條件
許多情況下我們的基礎(chǔ)版本產(chǎn)品,很難滿足用戶100%的需求,
而這些新需求又大部分是一些簡單的表單、查詢條件
如果是讓程序員去開發(fā)一個表單,添加幾個查詢條件,那確實很簡單
但用戶不是程序員,我們也不可能為每個用戶提供全面全天候的技術(shù)支持
考慮到成本與用戶體驗的問題通常會給用戶提供一個自定義表單與查詢條件的功能
自定義并不難,難的是對自定義表單的存儲、查詢、關(guān)聯(lián),以及對自定義查詢條件的支持
與上一條說的元數(shù)據(jù)管理一樣,我們在代碼實現(xiàn)環(huán)節(jié)還是不知道會有什么對象什么屬性
當(dāng)然也更不會有對應(yīng)的service, dao, mapper, VO/DTO/BO/DO/PO/POJO
Anyline的動態(tài)查詢類似這樣實現(xiàn)
service.query(類型(屬性集合),condition().add('對比方式','屬性','值');
5).物聯(lián)網(wǎng)環(huán)境(特別是像Cassandra、ClickHouse等列式數(shù)據(jù)庫 InfluxDB、TimescaleDB等時序數(shù)據(jù)庫)
與低代碼平臺類似都需要一種動態(tài)的結(jié)構(gòu),并且為了數(shù)據(jù)讀取的高效,數(shù)據(jù)在水平方向上變的更分散。
這與最終用戶需要顯示的格式完全不一樣,直接通過數(shù)據(jù)庫查詢出來的原始數(shù)據(jù)通常是類似這樣
時間戳 | KEY | VALUE |
1657330073131 | LAT | 39.917055 |
1657330073131 | LNG | 116.392191 |
1657330073132 | LAT | 39.917055 |
1657330073132 | LNG | 116.392191 |
1657330073133 | LAT | 39.917055 |
1657330073134 | LNG | 116.392191 |
而最終展示的界面可能是這樣:
時間戳 | LNG | LAT |
1657330073131 | 116.392191 | 39.917055 |
1657330073131 | 116.392191 | 39.917055 |
日期(向下合并) | 時間點1(向右合并) | 時間點2(向右合并) | 時間點…N | |||
LNG | LAT | LNG | LAT | LNG | LAT | |
01-01 | 116.392191 | 39.917055 | 116.392191 | 39.917055 | 116.392191 | 39.917055 |
01-02 | 116.392191 | 39.917055 | 116.392191 | 39.917055 | 116.392191 | 39.917055 |
當(dāng)然實戰(zhàn)中會比這更復(fù)雜,歷經(jīng)實戰(zhàn)的程序員一定體驗過什么是千變?nèi)f化、什么是刁鉆苛刻
數(shù)據(jù)庫中將不再有一一對應(yīng)的hello表格,java中也沒有對應(yīng)的Entity
可以想像的出來基于一個靜態(tài)結(jié)構(gòu)或者原始的Map,List結(jié)構(gòu)需要程序員負責(zé)多少體力
要在這個基礎(chǔ)上實現(xiàn)讓用戶自定義報表,那可能比把用戶培養(yǎng)成一個程序員還要困難
而一個有思想的程序員應(yīng)該會把以上問題抽象成簡單的行列轉(zhuǎn)換的問題
并在項目之前甚至沒有項目的時候就已經(jīng)解決之。
各種維度的轉(zhuǎn)換可以參考DataSet.pivot()的幾個重載 或示例代碼 anyline-simple-result
6).關(guān)于分頁查詢的數(shù)據(jù)存儲結(jié)構(gòu)
通過默認的方式查詢
- 無論是否分頁 都可以通過DataSet結(jié)構(gòu)接收數(shù)據(jù)
- 不同的是分頁后DataSet.PageNavi中會嵌入詳細的分頁信息
通過User.class查詢數(shù)據(jù)時
- 如果沒有分頁 可以通過List<User>>結(jié)構(gòu)接收數(shù)據(jù)
- 如果有分頁了 那需要通過Page<List<User>>結(jié)構(gòu)接收數(shù)據(jù)
- 簡單查詢個部門列表,還要根據(jù)分不分頁寫兩個接口嗎
7).數(shù)據(jù)加密
對于需要加密的數(shù)據(jù)經(jīng)常會遇到數(shù)字類型的ID
而加密后的數(shù)據(jù)類型通常是String類型,導(dǎo)致原對象無法存儲
就先更到這里了,有需要數(shù)據(jù)庫操作工具的朋友關(guān)注 轉(zhuǎn)發(fā) 評論之后私信我【數(shù)據(jù)庫】即可。