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