欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 下數(shù)據(jù)業(yè)務(wù)邏輯開發(fā)技術(shù) JOOQ 和 SPL

 更新時(shí)間:2022年09月13日 11:06:49   作者:歐子有話說(shuō)  
這篇文章主要為大家介紹了Java 下數(shù)據(jù)業(yè)務(wù)邏輯開發(fā)技術(shù) JOOQ 和 SPL詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

很多開源技術(shù)都可以在Java下實(shí)現(xiàn)以數(shù)據(jù)庫(kù)為核心的業(yè)務(wù)邏輯,其中JOOQ的計(jì)算能力比Hibernate強(qiáng),可移植性比MyBatis強(qiáng),受到越來(lái)越多的關(guān)注。esProc SPL是新晉的數(shù)據(jù)計(jì)算語(yǔ)言,同樣在計(jì)算能力和可移植性方面優(yōu)勢(shì)突出。下面對(duì)二者進(jìn)行多方面的比較,從中找出開發(fā)效率更高的數(shù)據(jù)業(yè)務(wù)邏輯開發(fā)技術(shù)。JOOQ商業(yè)版主要支持了商業(yè)數(shù)據(jù)庫(kù)和存儲(chǔ)過(guò)程,不在此次討論范圍。

語(yǔ)言特征

編程風(fēng)格

JOOQ支持完整的面向?qū)ο蟮木幊田L(fēng)格,可以將多個(gè)對(duì)象(方法)組合起來(lái),形成類似SQL的語(yǔ)法邏輯。JOOQ可以使用Java的Lambda表達(dá)式、函數(shù)調(diào)用接口和流程控制語(yǔ)法,理論上也支持面向函數(shù)和面向過(guò)程,但這些表達(dá)式\語(yǔ)法沒(méi)有為JOOQ的結(jié)構(gòu)化數(shù)據(jù)對(duì)象(Result)而設(shè)計(jì),使用時(shí)還不夠方便。

SPL支持面向?qū)ο?、面向函?shù)、面向過(guò)程的編程風(fēng)格,并進(jìn)行大幅簡(jiǎn)化。SPL有對(duì)象的概念,可以用點(diǎn)號(hào)訪問(wèn)屬性并進(jìn)行多步驟計(jì)算,但沒(méi)有繼承重載這些內(nèi)容。SPL的Lambda表達(dá)式比SQL更加簡(jiǎn)單易用,函數(shù)調(diào)用接口和流程控制語(yǔ)法專為結(jié)構(gòu)化數(shù)據(jù)對(duì)象(序表)而設(shè)計(jì),使用更加方便。

運(yùn)行模式

JOOQ是編譯執(zhí)行的Java代碼,性能高一些,靈活性較差。但JOOQ本身沒(méi)有計(jì)算能力,執(zhí)行后只生成SQL語(yǔ)句,再交由數(shù)據(jù)庫(kù)計(jì)算并返回結(jié)果,實(shí)際性能并不高,有些業(yè)務(wù)邏輯需要反復(fù)讀寫數(shù)據(jù)庫(kù),性能就更差了。SPL是解釋型語(yǔ)言,編碼更靈活,相同代碼性能會(huì)差一點(diǎn)。但是,SPL有不依賴數(shù)據(jù)庫(kù)的獨(dú)立計(jì)算能力,無(wú)需反復(fù)讀寫數(shù)據(jù)庫(kù),內(nèi)置大量時(shí)間復(fù)雜度更低的基礎(chǔ)運(yùn)算,計(jì)算性能經(jīng)常能超過(guò)編譯型語(yǔ)言。

外部類庫(kù)

JOOQ可以引入其他任意的第三方Java類庫(kù),用來(lái)彌補(bǔ)自身的短板,比如利用Stream增加自己的獨(dú)立計(jì)算能力,但這些類庫(kù)沒(méi)有為結(jié)構(gòu)化數(shù)據(jù)對(duì)象而設(shè)計(jì),提供的功能比較有限。SPL內(nèi)置專業(yè)的數(shù)據(jù)處理函數(shù),提供了大量開發(fā)效率更高、時(shí)間復(fù)雜度更低的基本運(yùn)算,通常不需要外部Java類庫(kù),特殊情況可在自定義函數(shù)中調(diào)用。

IDE和調(diào)試

兩者都有圖形化IDE和完整的調(diào)試功能。JOOQ使用Java IDE,好處是更通用,缺點(diǎn)是沒(méi)有為數(shù)據(jù)處理做優(yōu)化,無(wú)法方便地觀察結(jié)構(gòu)化數(shù)據(jù)對(duì)象。SPL的IDE專為數(shù)據(jù)處理而設(shè)計(jì),結(jié)構(gòu)化數(shù)據(jù)對(duì)象呈現(xiàn)為表格形式,觀察更加方便。

學(xué)習(xí)難度

JOOQ需要學(xué)習(xí)三種語(yǔ)法,SQL、通用Java、JOOQ。其中,SQL的語(yǔ)言能力要高于一般水平,才能轉(zhuǎn)化為JOOQ語(yǔ)法;開發(fā)時(shí)主要使用JOOQ語(yǔ)法,難度不高,但轉(zhuǎn)化過(guò)程較復(fù)雜;通用Java的語(yǔ)言能力可以低于一般水平。SPL的目標(biāo)是簡(jiǎn)化Java甚至SQL的編碼,無(wú)論入門學(xué)習(xí)還是深入開發(fā),難度都不高。但涉及到高性能計(jì)算時(shí)需要學(xué)習(xí)較多特有的算法,難度也會(huì)提高。

代碼量

SQL擅長(zhǎng)結(jié)構(gòu)化數(shù)據(jù)計(jì)算,語(yǔ)法較簡(jiǎn)練,代碼量較低,但為了把SQL翻譯成JOOQ,需要引入很多函數(shù),存在過(guò)度封裝的現(xiàn)象,實(shí)際代碼量較大。JOOQ的流程控制要借助Java語(yǔ)法,但Java語(yǔ)法沒(méi)有為結(jié)構(gòu)化數(shù)據(jù)對(duì)象而設(shè)計(jì),代碼量也不低。

SPL的表達(dá)能力強(qiáng)于SQL,遠(yuǎn)強(qiáng)于JOOQ,可用更低的代碼量實(shí)現(xiàn)結(jié)構(gòu)化數(shù)據(jù)計(jì)算,SPL的流程處理語(yǔ)句專為結(jié)構(gòu)化數(shù)據(jù)對(duì)象而設(shè)計(jì),代碼量低于Java。

結(jié)構(gòu)化數(shù)據(jù)對(duì)象

結(jié)構(gòu)化數(shù)據(jù)對(duì)象用于將數(shù)據(jù)庫(kù)表對(duì)象化,是數(shù)據(jù)處理和業(yè)務(wù)邏輯開發(fā)的基礎(chǔ),專業(yè)的結(jié)構(gòu)化數(shù)據(jù)對(duì)象可以方便地與數(shù)據(jù)庫(kù)交換數(shù)據(jù),支持豐富的計(jì)算函數(shù),并簡(jiǎn)化流程處理的難度。

定義

JOOQ的結(jié)構(gòu)化數(shù)據(jù)對(duì)象由記錄和記錄集合組成。記錄對(duì)象的種類很多,第一類是Record對(duì)象,適合字段的數(shù)量、類型、名字都是動(dòng)態(tài)生成的情況,Record雖然很靈活但面向?qū)ο蟮某潭容^低,用法比較麻煩,比如要通過(guò)getValue(M)來(lái)獲得第M個(gè)字段。

第二類是Record[N]對(duì)象,N從1到22,比如Record3,適合字段類型和字段數(shù)量已知但不超過(guò)22個(gè),而字段名是動(dòng)態(tài)生成的情況,Record[N]靈活性差些但面向?qū)ο蟮某潭壬愿?,用法方便些,比如可以通過(guò)valueM取得第M個(gè)字段。

第三類記錄對(duì)象由JOOQ的代碼工具根據(jù)庫(kù)表結(jié)構(gòu)生成,有幾個(gè)表就有幾個(gè)對(duì)象,字段的數(shù)量、類型、名字都和庫(kù)表字段嚴(yán)格對(duì)應(yīng),比如OrdersRecord、EmployeesRecord,這種記錄對(duì)象不靈活但面向?qū)ο蟮某潭群芨?,用法也很方便,可以直接通過(guò)字段名取字段。

第三類對(duì)應(yīng)庫(kù)表,可稱之為固定數(shù)據(jù)結(jié)構(gòu)的記錄對(duì)象,前兩類通常來(lái)自對(duì)庫(kù)表的查詢計(jì)算,可稱之為動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)的記錄對(duì)象。這三類比較常用,還有些不常用的記錄對(duì)象,比如用戶自定義記錄類型UDT,這里就不展開說(shuō)了。JOOQ的記錄對(duì)象種類繁多,用法差異較大,增加了開發(fā)的難度,這主要因?yàn)闃I(yè)務(wù)邏輯存在大量動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),而Java是編譯型語(yǔ)言,只擅長(zhǎng)表達(dá)固定數(shù)據(jù)結(jié)構(gòu),如果硬要表達(dá)動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),就只能設(shè)計(jì)復(fù)雜的接口規(guī)則,或者根據(jù)字段數(shù)量預(yù)定義大量對(duì)象。

JOOQ記錄集合的種類相對(duì)較少,常用的有原生對(duì)象Result,及其父類ArrayList,有時(shí)候也會(huì)用Stream。

SPL的結(jié)構(gòu)化數(shù)據(jù)對(duì)象同樣由記錄(Record)和記錄集合(序表)組成。SPL的記錄對(duì)象只有一種,主要因?yàn)镾PL是解釋型語(yǔ)言,動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)和固定數(shù)據(jù)結(jié)構(gòu)表達(dá)起來(lái)同樣方便,接口都很簡(jiǎn)單,沒(méi)必要分成多個(gè)。此外,記錄對(duì)象與單記錄集合雖然本質(zhì)不同,但業(yè)務(wù)意義相似,用起來(lái)容易混淆。SPL是解釋型語(yǔ)言,可以通過(guò)靈活的接口使兩者的外部用法保持一致,從而進(jìn)一步提高易用性。相反,JOOQ是編譯型語(yǔ)言,很難設(shè)計(jì)出這種靈活的接口,只能提供兩類不同的接口,分別用來(lái)處理記錄對(duì)象和單記錄集合。

讀數(shù)據(jù)庫(kù)

JOOQ讀取外部數(shù)據(jù)庫(kù)表,生成固定記錄集合:

java.sql.Connection conn = DriverManager.getConnection(url, userName, password);
DSLContext context = DSL.using(conn, SQLDialect.MYSQL);
Result<OrdersRecord> R1=context.select().from(ORDERS).fetchInto(ORDERS);

查詢外部數(shù)據(jù)庫(kù)表,生成動(dòng)態(tài)記錄集合:

Result<Record3<Integer,String,Double>>R2=context.select(ORDERS.SELLERID,ORDERS.CLIENT,ORDERS.AMOUNT).from(ORDERS).fetch();

動(dòng)態(tài)記錄集合的后續(xù)用法稍顯麻煩,但可以兼容固定記錄集合,下面文章中主要用動(dòng)態(tài)記錄集合。

SPL讀取或查詢外部數(shù)據(jù)庫(kù)表,生成序表:

 A
1=conn=connect("mysql8")
2=conn.query("select * from Orders")
3=conn.query("select SellerID,Client,Amount from Orders")

SPL不分固定記錄集合或動(dòng)態(tài)記錄集合,生成方法一致,后續(xù)用法相同。

寫數(shù)據(jù)庫(kù)

將處理后的結(jié)構(gòu)化數(shù)據(jù)對(duì)象持久化保存到數(shù)據(jù)庫(kù),JOOQ提供了三種函數(shù),分別是insert、update、delete。修改記錄r,再更新到數(shù)據(jù)庫(kù):

r.setValue(ORDERS.AMOUNT,r.getValue(ORDERS.AMOUNT).doubleValue()+100);
r.update();

上面是單條記錄的更新。要注意的是,數(shù)據(jù)庫(kù)表必須有主鍵,自動(dòng)生成的記錄類才會(huì)繼承UpdatableRecordImpl,只有繼承UpdatableRecordImpl的記錄類才支持update函數(shù)。

批量寫入數(shù)據(jù)庫(kù)是數(shù)據(jù)業(yè)務(wù)邏輯常見的場(chǎng)景,JOOQ也能實(shí)現(xiàn)。批量修改記錄集合T,并更新到數(shù)據(jù)庫(kù):

R1.forEach(r->{
 r.setValue(ORDERS.AMOUNT,r.getValue(ORDERS.AMOUNT).doubleValue()+100);});
R1.forEach(r->{
 r.update();});

上面代碼循環(huán)記錄集合,手工更新每一條記錄,從而實(shí)現(xiàn)對(duì)整體集合的更新。可以看到,JOOQ通過(guò)硬寫代碼實(shí)現(xiàn)批量寫入,沒(méi)有進(jìn)行封裝,很多時(shí)候不方便。如果一批記錄既有修改又有新增還有刪除,就必須區(qū)分三類記錄,再用不同的函數(shù)循環(huán)寫入,常見的辦法是繼承記錄類,新加一個(gè)“標(biāo)識(shí)”屬性予以區(qū)分,或者保存一個(gè)未修改的原始記錄集合T,將修改后的集合NT與原始集合進(jìn)行手工比對(duì)。無(wú)論哪種方法,手工實(shí)現(xiàn)的過(guò)程都很麻煩。

SPL對(duì)數(shù)據(jù)庫(kù)的寫入進(jìn)行了封裝,只用一個(gè)update函數(shù)就實(shí)現(xiàn)單條和批量記錄的新增、修改、刪除,且支持混合更新。比如:原序表為T,經(jīng)過(guò)增刪改一系列處理后的序表為NT,將變化結(jié)果持久化到數(shù)據(jù)庫(kù)的orders表:

conn.update(NT:T,orders)

訪問(wèn)字段

JOOQ讀取單條記錄的Client字段:

R1.get(0).getClient();
R1.get(0).get(ORDERS.CLIENT);

上面代碼體現(xiàn)了JOOQ的核心優(yōu)勢(shì):支持純粹的面向?qū)ο蟮淖侄卧L問(wèn)方式,不摻雜字符串、數(shù)字常量,或其他非Java的表達(dá)式,代碼風(fēng)格高度統(tǒng)一。遺憾之處在于,上面代碼只適用于固定結(jié)構(gòu)化數(shù)據(jù)對(duì)象。如果是查詢計(jì)算生成的動(dòng)態(tài)記錄對(duì)象,就只能使用字符串字段名或數(shù)字序號(hào)訪問(wèn)字段:

R2.get(0).get("Client");
R2.get(0).get(1);

動(dòng)態(tài)記錄對(duì)象更加普遍,上面的字段訪問(wèn)方式不算純粹的面向?qū)ο?,代碼風(fēng)格不一致,不支持自動(dòng)補(bǔ)全,編寫時(shí)普遍麻煩。

SPL支持純粹的面向?qū)ο蟮淖侄卧L問(wèn)方式,不分固定或動(dòng)態(tài),編寫時(shí)普遍方便:

T(1).Client

當(dāng)然也支持字符串字段名或數(shù)字序號(hào)訪問(wèn)字段:

T(1).field(2)
T(1).field("Client")

SPL在面向?qū)ο蠓矫娓蛹兇?,風(fēng)格更統(tǒng)一,編寫代碼更加方便。此外,SPL提供了很多JOOQ不支持的便利功能:默認(rèn)字段名,可以用點(diǎn)號(hào)直接訪問(wèn),比如取第2個(gè)字段:T(1).#2;取多個(gè)字段,返回集合的集合:T.([Client,Amount])

有序訪問(wèn)

有序訪問(wèn)是業(yè)務(wù)邏輯開發(fā)的難點(diǎn)之一,JOOQ的記錄集合繼承自Java的有序集合ArrayList,具備一定的有序訪問(wèn)能力,支持按下標(biāo)取記錄和按區(qū)間取記錄:

R.get(3)
R.subList(3,5);

再進(jìn)一步的功能,就需要硬編碼實(shí)現(xiàn)了,比如后3條:

Collections.reverse(R);
R.subList(0,3);

至于按位置集合取記錄、步進(jìn)取記錄等功能,硬編碼就更麻煩了。

SPL序表同樣是有序集合,提供了順序相關(guān)的基本功能,比如按下標(biāo)取、按區(qū)間取:

T(3)
T.to(3,5)

序表是專業(yè)的結(jié)構(gòu)化數(shù)據(jù)對(duì)象,許多順序相關(guān)的高級(jí)功能JOOQ Result沒(méi)有支持,序表則直接提供了,比如按倒數(shù)序號(hào)取記錄,可以直接用負(fù)號(hào)表示:

T.m(-3)							//倒數(shù)第3條
T.m(to(-3,-5))						//倒數(shù)區(qū)間

再比如按位置集合取記錄、步進(jìn)取記錄:

T.m(1,3,5,7:10)						//序號(hào)是1、3、5、7-10的記錄
T.m(-1,-3,-5)						//倒數(shù)第1,3,5條
T.step(2,1)						//每2條取第1條(等價(jià)于奇數(shù)位置)

結(jié)構(gòu)化數(shù)據(jù)計(jì)算

結(jié)構(gòu)化數(shù)據(jù)計(jì)算能力是數(shù)據(jù)業(yè)務(wù)邏輯的核心功能,下面從簡(jiǎn)單到復(fù)雜選取幾個(gè)常見題目,比較JOOQ和SPL的計(jì)算代碼。

改名

//等價(jià)的SQL
select SellerID eid,Amount amt from Orders
//JOOQ
context.select(ORDERS.SELLERID.as("eid"),ORDERS.AMOUNT.as("amt")).from(ORDERS).fetch()
//SPL
Orders.new(SellerID:EID, Amount:amt)

JOOQ的語(yǔ)法邏輯與SQL基本一致,可以達(dá)到用面向?qū)ο蟮姆绞侥MSQL的目的,這是JOOQ的重要優(yōu)點(diǎn)。相應(yīng)的也有缺點(diǎn),JOOQ的一項(xiàng)運(yùn)算需要多個(gè)函數(shù)的組合才能實(shí)現(xiàn),每個(gè)函數(shù)都有自己的參數(shù)和語(yǔ)法規(guī)則,學(xué)習(xí)和編寫難度較大。此外,很多函數(shù)里的字段名必須附帶表名,即使單表計(jì)算也是如此,這說(shuō)明JOOQ的語(yǔ)法不夠?qū)I(yè),還有很大的改進(jìn)空間。

SPL直接用面向?qū)ο蟮恼Z(yǔ)法實(shí)現(xiàn)計(jì)算,一項(xiàng)運(yùn)算對(duì)應(yīng)一個(gè)函數(shù),引用字段不必附帶表名,語(yǔ)法更專業(yè),代碼更簡(jiǎn)短。

條件查詢

//等價(jià)的SQL
select * from Orders where 
	((SellerID=2 and Amount<3000) or (SellerID=3 and Amount>=2000 and Amount<5000)) 
	and
	year(OrderDate)>2010
//JOOQ
context.select().from(ORDERS)
.where(
	((ORDERS.SELLERID.equal(2).and(ORDERS.AMOUNT.lessThan(3000.0)))
	.or((ORDERS.SELLERID.equal(3).and(ORDERS.AMOUNT.greaterOrEqual(2000.0).and(ORDERS.AMOUNT.lessThan(5000.0))))))
	.and(year(ORDERS.ORDERDATE).greaterThan(2012)))
.fetch();
//SPL
Orders.select(
	((SellerID==2 && Amount<3000) || (SellerID==3 && Amount>=2000 && Amount<5000)) 
	&&
	year(OrderDate)>2010)

SQL的條件表達(dá)式本身足夠簡(jiǎn)單,JOOQ雖然在模擬SQL,但對(duì)條件表達(dá)式進(jìn)行了過(guò)度封裝,函數(shù)數(shù)量過(guò)多,多層括號(hào)難閱讀,遠(yuǎn)不如SQL好理解。SPL用一個(gè)函數(shù)實(shí)現(xiàn)條件查詢,條件表達(dá)式簡(jiǎn)短易讀。

分組匯總

//等價(jià)的SQL:
select Client, extract(year from OrderDate) y,count(1) cnt
from Orders
group by Client, extract(year from OrderDate)
having amt<20000
//JOOQ
context.select(ORDERS.CLIENT,year(ORDERS.ORDERDATE).as("y"),sum(ORDERS.AMOUNT).as("amt"),count(one()).as("cnt"))
.from(ORDERS)
.groupBy(ORDERS.CLIENT,year(ORDERS.ORDERDATE))
.having(field("amt").lessThan(20000)).fetch();
//SPL
Orders.groups(Client,year(OrderDate):y;sum(Amount):amt,count(1):cnt)
.select(amt<20000)

為了模擬SQL,JOOQ使用了很多函數(shù),規(guī)則很復(fù)雜,導(dǎo)致代碼過(guò)長(zhǎng)。SPL直接用面向?qū)ο蟮恼Z(yǔ)法,規(guī)則簡(jiǎn)單,代碼更短。

前面都是較簡(jiǎn)單計(jì)算,類似的計(jì)算還包括排序、去重、關(guān)聯(lián)、集合交并差等計(jì)算,這里不再一一列舉,總的來(lái)說(shuō),JOOQ進(jìn)行簡(jiǎn)單計(jì)算時(shí)比SQL和SPL代碼長(zhǎng),很多時(shí)候不易理解,開發(fā)效率較低。

各組前3名

//等價(jià)的SQL
select * from (select *, row_number() over (partition by Client order by Amount) rn from Orders) T where rn<=3
//JOOQ
WindowDefinition CA = name("CA").as(partitionBy(ORDERS.CLIENT).orderBy(ORDERS.AMOUNT));
context.select().from(select(ORDERS.ORDERID,ORDERS.CLIENT,ORDERS.SELLERID,ORDERS.AMOUNT,ORDERS.ORDERDATE,rowNumber().over(CA).as("rn")).from(ORDERS).window(CA) ).where(field("rn").lessOrEqual(3)).fetch();
//SPL
Orders.group(Client).(~.top(3;Amount)).conj()

這道題目稍有難度,JOOQ雖然模擬出了SQL,但使用了很多函數(shù),代碼長(zhǎng)度遠(yuǎn)超SQL,語(yǔ)法也越來(lái)越不像SQL,編寫理解更加困難。SPL先對(duì)客戶分組,再求各組(即~)的前3名,最后合并各組計(jì)算結(jié)果,不僅代碼更簡(jiǎn)短,且更易理解。

JOOQ使用了窗口函數(shù),只適合特定版本的數(shù)據(jù)庫(kù),比如MySQL8,不能通用于其他版本的數(shù)據(jù)庫(kù),要想在MySQL5下實(shí)現(xiàn)同樣的計(jì)算,代碼改動(dòng)非常麻煩。SPL有獨(dú)立計(jì)算能力,代碼可通用于任何數(shù)據(jù)庫(kù)。

某支股票最大連續(xù)上漲天數(shù)

JOOQ:

WindowDefinition woDay1 = name("woDay").as(orderBy(APPL.DAY));
Table<?>T0=table(select(APPL.DAY.as("DAY"),when(APPL.PRICE.greaterThan(lag(APPL.PRICE).over(woDay1)),0).otherwise(1).as("risingflag")).from(APPL).window(woDay1)).as("T0");
WindowDefinition woDay2 = name("woDay1").as(orderBy(T0.field("DAY")));
Table<?>T1=table(select(sum(T0.field("risingflag").cast(java.math.BigDecimal.class)).over(woDay2).as("norisingdays")).from(T0).window(woDay2)).as("T1");
Table<?>T2=table(select(count(one()).as("continuousdays")).from(T1).groupBy(T1.field("norisingdays"))).as("T2");
Result<?> result=context.select(max(T2.field("continuousdays"))).from(T2).fetch();

這個(gè)問(wèn)題難度較高,需要綜合運(yùn)用多種簡(jiǎn)單計(jì)算。JOOQ很難直接表達(dá)連續(xù)上漲的概念,只能使用技巧變相實(shí)現(xiàn),即通過(guò)累計(jì)不漲天數(shù)來(lái)計(jì)算連續(xù)上漲天數(shù)。具體是,先按時(shí)間順序給每條記錄打漲跌標(biāo)記risingflag,如果下跌,則標(biāo)為1,如果上漲,則標(biāo)為0;再按時(shí)間順序累計(jì)每條記錄的不漲天數(shù)norisingdays,只有當(dāng)前記錄下跌時(shí),這個(gè)數(shù)字才會(huì)變大,如果當(dāng)前記錄上漲,則這個(gè)數(shù)字不變;再按不漲天數(shù)norisingdays分組,求各組記錄數(shù),顯然,連續(xù)下跌的一批記錄的norisingdays不同,每條記錄都會(huì)分到不同的組,該組計(jì)數(shù)為1,這個(gè)值不是解題目標(biāo),而連續(xù)上漲的一批記錄的norisingdays相同,可以分到同一組,該組計(jì)數(shù)即連續(xù)上漲的天數(shù),這個(gè)值是解題目標(biāo);最后用max函數(shù)求出最大的連續(xù)上漲天數(shù)。

JOOQ的編程過(guò)程是先寫SQL,再翻譯成JOOQ,對(duì)于簡(jiǎn)單計(jì)算來(lái)說(shuō),SQL比較好寫,翻譯也不會(huì)太難,但對(duì)于本題這種綜合性計(jì)算來(lái)說(shuō),計(jì)算邏輯的技巧性比較強(qiáng),SQL不好寫,翻譯的難度更大。此外,JOOQ表面上是方便調(diào)試的Java,但本質(zhì)卻是SQL,和SQL一樣難以調(diào)試,這又為將來(lái)的維護(hù)工作埋下了大坑。

SPL代碼簡(jiǎn)單多了:

APPL.sort(day).group@i(price<price[-1]).max(~.count())

這條SPL語(yǔ)句的計(jì)算邏輯和JOOQ是相同的,也是將連漲記錄分到同一組中再求最大的組成員數(shù),但表達(dá)起來(lái)要方便很多。group@i()表示遍歷序表,如果符合條件則開始新的一組(并使之前的記錄分到同一組),price<price[-1]這個(gè)條件表示股價(jià)下跌,則之前股價(jià)上漲的記錄會(huì)分到同一組。[-1]表示上一條,是相對(duì)位置的表示方法,price[-1]表示上一個(gè)交易日的股價(jià),比整體移行(lag.over)更直觀。

相對(duì)位置屬于有序計(jì)算,SPL是專業(yè)的結(jié)構(gòu)化計(jì)算語(yǔ)言,支持有序計(jì)算,代碼因此更簡(jiǎn)單。除了有序集合,SPL還可以簡(jiǎn)化多種復(fù)雜計(jì)算,包括多步驟計(jì)算、集合計(jì)算、復(fù)雜的關(guān)聯(lián)計(jì)算。相反,這幾類計(jì)算都是JOOQ不擅長(zhǎng)的,通常要通過(guò)特殊技巧實(shí)現(xiàn),代碼很難寫。

SPL函數(shù)選項(xiàng)和層次參數(shù)

值得一提的是,為了進(jìn)一步提高開發(fā)效率,SPL還提供了獨(dú)特的函數(shù)語(yǔ)法。有大量功能類似的函數(shù)時(shí),JOOQ只能用不同的名字或者參數(shù)進(jìn)行區(qū)分,使用不太方便。而SPL提供了非常獨(dú)特的函數(shù)選項(xiàng),使功能相似的函數(shù)可以共用一個(gè)函數(shù)名,只用函數(shù)選項(xiàng)區(qū)分差別。比如,select函數(shù)的基本功能是過(guò)濾,如果只過(guò)濾出符合條件的第1條記錄,可使用選項(xiàng)@1:

T.select@1(Amount>1000)

對(duì)有序數(shù)據(jù)用二分法進(jìn)行快速過(guò)濾,使用@b:

T.select@b(Amount>1000)

函數(shù)選項(xiàng)還可以組合搭配,比如:

Orders.select@1b(Amount>1000)

有些函數(shù)的參數(shù)很復(fù)雜,可能會(huì)分成多層。JOOQ對(duì)此并沒(méi)有特別的語(yǔ)法方案,只能拆成多個(gè)函數(shù)互相嵌套,盡力模擬成SQL語(yǔ)法,導(dǎo)致代碼冗長(zhǎng)繁瑣。而SPL創(chuàng)造性地發(fā)明了層次參數(shù)簡(jiǎn)化了復(fù)雜參數(shù)的表達(dá),通過(guò)分號(hào)、逗號(hào)、冒號(hào)自高而低將參數(shù)分為三層。比如關(guān)聯(lián)兩個(gè)表:

join(Orders:o,SellerId ; Employees:e,EId)

流程處理

JOOQ支持部分存儲(chǔ)過(guò)程語(yǔ)法,包括循環(huán)語(yǔ)句和判斷語(yǔ)句,但這屬于商業(yè)版功能,且權(quán)限要求高、安全隱患大,難以移植,一般很少用到。除了存儲(chǔ)過(guò)程,JOOQ還可以利用Java實(shí)現(xiàn)流程處理,對(duì)數(shù)據(jù)庫(kù)沒(méi)有權(quán)限要求,安全隱患小,且可無(wú)縫移植。比如,根據(jù)規(guī)則計(jì)算獎(jiǎng)金:

Orders.forEach(r->{
	Double amount=r.getValue(ORDERS.AMOUNT);
	if (amount>10000) {
		r.setValue(ORDERS.BONUS), amount * 0.05);
	}else if(amount>=5000 && amount<10000){
		r.setValue(ORDERS.BONUS),amount*0.03);
	}else if(amount>=2000 && amount<5000){
		r.setValue(ORDERS.BONUS),amount*0.02);
	}
});

forEach循環(huán)函數(shù)針對(duì)JOOQ的結(jié)構(gòu)化數(shù)據(jù)對(duì)象進(jìn)行了優(yōu)化,可以通過(guò)Lambda表達(dá)式簡(jiǎn)化循環(huán)結(jié)構(gòu)的定義,可以方便地處理集合對(duì)象的每個(gè)成員(代碼中的循環(huán)變量r)。forEach函數(shù)配合Lambda語(yǔ)法,整體代碼要比傳統(tǒng)循環(huán)語(yǔ)句簡(jiǎn)單些。但也應(yīng)該注意到,forEach函數(shù)里使用字段需要附帶循環(huán)變量名,對(duì)單表計(jì)算來(lái)說(shuō)是多余的,同樣使用Lambda語(yǔ)法的SQL就可以省略變量名。此外,定義循環(huán)變量名也是多余的,SQL就不用定義。這些缺點(diǎn)都說(shuō)明JOOQ在流程處理方面還不夠?qū)I(yè),代碼還有很大的優(yōu)化空間。

SPL也有針對(duì)結(jié)構(gòu)化數(shù)據(jù)對(duì)象進(jìn)行優(yōu)化的循環(huán)函數(shù),直接用括號(hào)表示。同樣根據(jù)規(guī)則計(jì)算獎(jiǎng)金:

Orders.(Bonus=if(Amount>10000,Amount*0.05,
	if(Amount>5000 && Amount<10000, Amount*0.03,
	if(Amount>=2000 && Amount<5000, Amount*0.02)
)))

SPL的循環(huán)函數(shù)同樣支持Lambda表達(dá)式,而且接口更簡(jiǎn)單,不必定義循環(huán)變量,使用字段時(shí)不必引用變量名,比JOOQ更方便,專業(yè)性也更強(qiáng)。除了循環(huán)函數(shù),SPL還有更多專業(yè)的流程處理功能,比如:每輪循環(huán)取一批而不是一條記錄;某字段值變化時(shí)循環(huán)一輪。

SPL專業(yè)的流程處理功能,配合專業(yè)的結(jié)構(gòu)化數(shù)據(jù)對(duì)象和結(jié)構(gòu)化數(shù)據(jù)計(jì)算能力,可大幅提高數(shù)據(jù)業(yè)務(wù)邏輯的開發(fā)效率。一個(gè)完整的例子:計(jì)算出獎(jiǎng)金,并向數(shù)據(jù)庫(kù)插入新記錄。JOOQ需要生成多個(gè)文件,編寫大段代碼才能實(shí)現(xiàn),SPL就簡(jiǎn)單多了:

 ABC
1=db=connect@e("dbName") /連接數(shù)據(jù)庫(kù),開啟事務(wù)
2=db.query@1("select sum(Amount) from sales where sellerID=? and year(OrderDate)=? and month(OrderDate)=?", p_SellerID,year(now()),month(now())) /查詢當(dāng)月銷售額
3=if(A2>=10000 :200, A2<10000 && A2>=2000 :100, 0) /本月累計(jì)獎(jiǎng)金
4=p_Amount*0.05 /本單固定獎(jiǎng)金
5=BONUS=A3+A4 /總獎(jiǎng)金
6=create(ORDERID,CLIENT,SELLERID,AMOUNT,BONUS,ORDERDATE) /創(chuàng)建訂單的數(shù)據(jù)結(jié)構(gòu)
7=A6.record([p_OrderID,p_Client,p_SellerID,p_Amount,BONUS,date(now())]) /生成一條訂單記錄
8>db.update@ik(A7,sales;ORDERID) /嘗試寫入庫(kù)表
9=db.error() /入庫(kù)結(jié)果
10if A9==0>A1.commit()/成功,則提交事務(wù)
11else>A1.rollback()/失敗,則回滾事務(wù)
12>db.close() /關(guān)閉數(shù)據(jù)庫(kù)連接
13return A9 /返回入庫(kù)結(jié)果

利用SPL的流程處理語(yǔ)句,可以實(shí)現(xiàn)存儲(chǔ)過(guò)程的所有能力,包括游標(biāo)的循環(huán)和判斷。SPL不依賴數(shù)據(jù)庫(kù),不需要數(shù)據(jù)庫(kù)權(quán)限,沒(méi)有安全隱患,相當(dāng)于庫(kù)外的存儲(chǔ)過(guò)程,同時(shí),這些功能也都是開源的。

應(yīng)用結(jié)構(gòu)

Java集成

JOOQ本身就是Java,可被其他Java代碼直接調(diào)用。

SPL是基于JVM的數(shù)據(jù)計(jì)算語(yǔ)言,提供了易用的JDBC接口,可被JAVA代碼無(wú)縫集成。比如,將業(yè)務(wù)邏輯代碼存為腳本文件,在JAVA中以存儲(chǔ)過(guò)程的形式調(diào)用文件名:

Class.forName("com.esproc.jdbc.InternalDriver");
Connection connection =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("call genBonus()");

熱部署

JOOQ(Java)是編譯型語(yǔ)言,不支持熱部署,修改代碼后需要重新編譯并重啟整個(gè)應(yīng)用,加大了維護(hù)難度,降低了系統(tǒng)穩(wěn)定性。

SPL是解釋型語(yǔ)言,代碼以腳本文件的形式外置于JAVA,支持熱部署,修改后不必編譯,也不必重啟應(yīng)用。由于SPL代碼不依賴JAVA,業(yè)務(wù)邏輯和前端代碼物理分離,耦合性也更低。

代碼移植

JOOQ的部分代碼可以移植,比不可移植的MyBatis方便。比如業(yè)務(wù)邏輯中常用于分頁(yè)的limit(M).offset(N),在Oracle11g數(shù)據(jù)庫(kù)下會(huì)被翻譯為rownum子查詢;如果數(shù)據(jù)庫(kù)改為MSSQL2012,只要重新生成并部署實(shí)體類,不必修改業(yè)務(wù)邏輯,同樣的代碼就會(huì)翻譯成offset next語(yǔ)句。

能夠移植的代碼畢竟是少數(shù),大部分JOOQ代碼都是不可以移植的,比如前面例子里的窗口函數(shù)。移植時(shí)需要讀懂原JOOQ代碼,反翻譯成原SQL,再改成新SQL,最后翻譯成新JOOQ代碼,過(guò)程較繁難度較高。業(yè)務(wù)邏輯普遍具有復(fù)雜性,移植工作就更難了。

SPL具有獨(dú)立計(jì)算能力,不必借用SQL,憑借豐富的內(nèi)置函數(shù)庫(kù)就能實(shí)現(xiàn)復(fù)雜的結(jié)構(gòu)化數(shù)據(jù)計(jì)算,計(jì)算代碼可在數(shù)據(jù)庫(kù)間無(wú)縫移植。在數(shù)據(jù)庫(kù)取數(shù)代碼中,SPL也要執(zhí)行方言SQL生成序表,雖然取數(shù)SQL比較簡(jiǎn)單,手工移植不難,但仍有一定工作量,為了使取數(shù)代碼便于移植,SPL專門提供了不依賴特定數(shù)據(jù)庫(kù)的通用SQL,可在主流數(shù)據(jù)庫(kù)間無(wú)縫移植。

通過(guò)多方面的比較可知:JOOQ可以進(jìn)行較簡(jiǎn)單的查詢統(tǒng)計(jì),但對(duì)于較復(fù)雜的業(yè)務(wù)邏輯開發(fā)就顯得比較繁瑣,尤其是有序計(jì)算、多步驟計(jì)算、集合計(jì)算、復(fù)雜的關(guān)聯(lián)查詢,存在翻譯SQL的工作量大,代碼冗長(zhǎng),難以修改,難以移植等問(wèn)題。SPL語(yǔ)法簡(jiǎn)練、表達(dá)效率高、代碼移植方便,結(jié)構(gòu)化數(shù)據(jù)對(duì)象更專業(yè),函數(shù)更豐富且計(jì)算能力更強(qiáng),流程處理更方便,開發(fā)效率遠(yuǎn)高于JOOQ。

以上就是Java 下數(shù)據(jù)業(yè)務(wù)邏輯開發(fā)技術(shù) JOOQ 和 SPL的詳細(xì)內(nèi)容,更多關(guān)于Java數(shù)據(jù)業(yè)務(wù)邏輯JOOQ SPL的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java字符串?dāng)?shù)組進(jìn)行大小排序的簡(jiǎn)單實(shí)現(xiàn)

    java字符串?dāng)?shù)組進(jìn)行大小排序的簡(jiǎn)單實(shí)現(xiàn)

    下面小編就為大家?guī)?lái)一篇java字符串?dāng)?shù)組進(jìn)行大小排序的簡(jiǎn)單實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-09-09
  • 為SpringBoot服務(wù)添加HTTPS證書的方法

    為SpringBoot服務(wù)添加HTTPS證書的方法

    這篇文章主要介紹了為SpringBoot服務(wù)添加HTTPS證書的方法,幫助大家更好的理解和使用springBoot框架,感興趣的朋友可以了解下
    2020-10-10
  • java web上傳文件和下載文件代碼片段分享

    java web上傳文件和下載文件代碼片段分享

    這篇文章主要為大家詳細(xì)介紹了java web上傳文件和下載文件代碼片段,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • SpringCloud微服務(wù)開發(fā)基于RocketMQ實(shí)現(xiàn)分布式事務(wù)管理詳解

    SpringCloud微服務(wù)開發(fā)基于RocketMQ實(shí)現(xiàn)分布式事務(wù)管理詳解

    分布式事務(wù)是在微服務(wù)開發(fā)中經(jīng)常會(huì)遇到的一個(gè)問(wèn)題,之前的文章中我們已經(jīng)實(shí)現(xiàn)了利用Seata來(lái)實(shí)現(xiàn)強(qiáng)一致性事務(wù),其實(shí)還有一種廣為人知的方案就是利用消息隊(duì)列來(lái)實(shí)現(xiàn)分布式事務(wù),保證數(shù)據(jù)的最終一致性,也就是我們常說(shuō)的柔性事務(wù)
    2022-09-09
  • 詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)

    詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)

    這篇文章主要介紹了詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Java并發(fā)系列之CountDownLatch源碼分析

    Java并發(fā)系列之CountDownLatch源碼分析

    這篇文章主要為大家詳細(xì)介紹了Java并發(fā)系列之CountDownLatch源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 深入理解Java動(dòng)態(tài)代理與靜態(tài)代理

    深入理解Java動(dòng)態(tài)代理與靜態(tài)代理

    這篇文章主要介紹了深入理解Java動(dòng)態(tài)代理與靜態(tài)代理,靜態(tài)代理,代理類和被代理的類實(shí)現(xiàn)了同樣的接口,代理類同時(shí)持有被代理類的引用,動(dòng)態(tài)代理的根據(jù)實(shí)現(xiàn)方式的不同可以分為JDK動(dòng)態(tài)代理和CGlib動(dòng)態(tài)代理
    2022-06-06
  • 淺析Java中的訪問(wèn)控制權(quán)限

    淺析Java中的訪問(wèn)控制權(quán)限

    這篇文章主要介紹了淺析Java中的訪問(wèn)控制權(quán)限,在Java中,提供了四種訪問(wèn)權(quán)限控制,分別是默認(rèn)訪問(wèn)權(quán)限、public、private以及protected,感興趣的小伙伴們可以參考一下
    2016-02-02
  • 一文詳解Java中的Stream的匯總和分組操作

    一文詳解Java中的Stream的匯總和分組操作

    這篇文章主要為大家詳細(xì)介紹了Java8中的Stream的匯總和分組的操作,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定幫助,需要的可以參考一下
    2022-09-09
  • java微信開發(fā)API第一步 服務(wù)器接入

    java微信開發(fā)API第一步 服務(wù)器接入

    這篇文章主要為大家分享了java微信開發(fā)API的第一步操作服務(wù)器接入,感興趣的小伙伴們可以參考一下
    2016-05-05

最新評(píng)論