Java 下數(shù)據業(yè)務邏輯開發(fā)技術 JOOQ 和 SPL
引言
很多開源技術都可以在Java下實現(xiàn)以數(shù)據庫為核心的業(yè)務邏輯,其中JOOQ的計算能力比Hibernate強,可移植性比MyBatis強,受到越來越多的關注。esProc SPL是新晉的數(shù)據計算語言,同樣在計算能力和可移植性方面優(yōu)勢突出。下面對二者進行多方面的比較,從中找出開發(fā)效率更高的數(shù)據業(yè)務邏輯開發(fā)技術。JOOQ商業(yè)版主要支持了商業(yè)數(shù)據庫和存儲過程,不在此次討論范圍。
語言特征
編程風格
JOOQ支持完整的面向對象的編程風格,可以將多個對象(方法)組合起來,形成類似SQL的語法邏輯。JOOQ可以使用Java的Lambda表達式、函數(shù)調用接口和流程控制語法,理論上也支持面向函數(shù)和面向過程,但這些表達式\語法沒有為JOOQ的結構化數(shù)據對象(Result)而設計,使用時還不夠方便。
SPL支持面向對象、面向函數(shù)、面向過程的編程風格,并進行大幅簡化。SPL有對象的概念,可以用點號訪問屬性并進行多步驟計算,但沒有繼承重載這些內容。SPL的Lambda表達式比SQL更加簡單易用,函數(shù)調用接口和流程控制語法專為結構化數(shù)據對象(序表)而設計,使用更加方便。
運行模式
JOOQ是編譯執(zhí)行的Java代碼,性能高一些,靈活性較差。但JOOQ本身沒有計算能力,執(zhí)行后只生成SQL語句,再交由數(shù)據庫計算并返回結果,實際性能并不高,有些業(yè)務邏輯需要反復讀寫數(shù)據庫,性能就更差了。SPL是解釋型語言,編碼更靈活,相同代碼性能會差一點。但是,SPL有不依賴數(shù)據庫的獨立計算能力,無需反復讀寫數(shù)據庫,內置大量時間復雜度更低的基礎運算,計算性能經常能超過編譯型語言。
外部類庫
JOOQ可以引入其他任意的第三方Java類庫,用來彌補自身的短板,比如利用Stream增加自己的獨立計算能力,但這些類庫沒有為結構化數(shù)據對象而設計,提供的功能比較有限。SPL內置專業(yè)的數(shù)據處理函數(shù),提供了大量開發(fā)效率更高、時間復雜度更低的基本運算,通常不需要外部Java類庫,特殊情況可在自定義函數(shù)中調用。
IDE和調試
兩者都有圖形化IDE和完整的調試功能。JOOQ使用Java IDE,好處是更通用,缺點是沒有為數(shù)據處理做優(yōu)化,無法方便地觀察結構化數(shù)據對象。SPL的IDE專為數(shù)據處理而設計,結構化數(shù)據對象呈現(xiàn)為表格形式,觀察更加方便。
學習難度
JOOQ需要學習三種語法,SQL、通用Java、JOOQ。其中,SQL的語言能力要高于一般水平,才能轉化為JOOQ語法;開發(fā)時主要使用JOOQ語法,難度不高,但轉化過程較復雜;通用Java的語言能力可以低于一般水平。SPL的目標是簡化Java甚至SQL的編碼,無論入門學習還是深入開發(fā),難度都不高。但涉及到高性能計算時需要學習較多特有的算法,難度也會提高。
代碼量
SQL擅長結構化數(shù)據計算,語法較簡練,代碼量較低,但為了把SQL翻譯成JOOQ,需要引入很多函數(shù),存在過度封裝的現(xiàn)象,實際代碼量較大。JOOQ的流程控制要借助Java語法,但Java語法沒有為結構化數(shù)據對象而設計,代碼量也不低。
SPL的表達能力強于SQL,遠強于JOOQ,可用更低的代碼量實現(xiàn)結構化數(shù)據計算,SPL的流程處理語句專為結構化數(shù)據對象而設計,代碼量低于Java。
結構化數(shù)據對象
結構化數(shù)據對象用于將數(shù)據庫表對象化,是數(shù)據處理和業(yè)務邏輯開發(fā)的基礎,專業(yè)的結構化數(shù)據對象可以方便地與數(shù)據庫交換數(shù)據,支持豐富的計算函數(shù),并簡化流程處理的難度。
定義
JOOQ的結構化數(shù)據對象由記錄和記錄集合組成。記錄對象的種類很多,第一類是Record對象,適合字段的數(shù)量、類型、名字都是動態(tài)生成的情況,Record雖然很靈活但面向對象的程度較低,用法比較麻煩,比如要通過getValue(M)來獲得第M個字段。
第二類是Record[N]對象,N從1到22,比如Record3,適合字段類型和字段數(shù)量已知但不超過22個,而字段名是動態(tài)生成的情況,Record[N]靈活性差些但面向對象的程度稍高,用法方便些,比如可以通過valueM取得第M個字段。
第三類記錄對象由JOOQ的代碼工具根據庫表結構生成,有幾個表就有幾個對象,字段的數(shù)量、類型、名字都和庫表字段嚴格對應,比如OrdersRecord、EmployeesRecord,這種記錄對象不靈活但面向對象的程度很高,用法也很方便,可以直接通過字段名取字段。
第三類對應庫表,可稱之為固定數(shù)據結構的記錄對象,前兩類通常來自對庫表的查詢計算,可稱之為動態(tài)數(shù)據結構的記錄對象。這三類比較常用,還有些不常用的記錄對象,比如用戶自定義記錄類型UDT,這里就不展開說了。JOOQ的記錄對象種類繁多,用法差異較大,增加了開發(fā)的難度,這主要因為業(yè)務邏輯存在大量動態(tài)數(shù)據結構,而Java是編譯型語言,只擅長表達固定數(shù)據結構,如果硬要表達動態(tài)數(shù)據結構,就只能設計復雜的接口規(guī)則,或者根據字段數(shù)量預定義大量對象。
JOOQ記錄集合的種類相對較少,常用的有原生對象Result,及其父類ArrayList,有時候也會用Stream。
SPL的結構化數(shù)據對象同樣由記錄(Record)和記錄集合(序表)組成。SPL的記錄對象只有一種,主要因為SPL是解釋型語言,動態(tài)數(shù)據結構和固定數(shù)據結構表達起來同樣方便,接口都很簡單,沒必要分成多個。此外,記錄對象與單記錄集合雖然本質不同,但業(yè)務意義相似,用起來容易混淆。SPL是解釋型語言,可以通過靈活的接口使兩者的外部用法保持一致,從而進一步提高易用性。相反,JOOQ是編譯型語言,很難設計出這種靈活的接口,只能提供兩類不同的接口,分別用來處理記錄對象和單記錄集合。
讀數(shù)據庫
JOOQ讀取外部數(shù)據庫表,生成固定記錄集合:
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ù)據庫表,生成動態(tài)記錄集合:
Result<Record3<Integer,String,Double>>R2=context.select(ORDERS.SELLERID,ORDERS.CLIENT,ORDERS.AMOUNT).from(ORDERS).fetch();
動態(tài)記錄集合的后續(xù)用法稍顯麻煩,但可以兼容固定記錄集合,下面文章中主要用動態(tài)記錄集合。
SPL讀取或查詢外部數(shù)據庫表,生成序表:
A | |
---|---|
1 | =conn=connect("mysql8") |
2 | =conn.query("select * from Orders") |
3 | =conn.query("select SellerID,Client,Amount from Orders") |
SPL不分固定記錄集合或動態(tài)記錄集合,生成方法一致,后續(xù)用法相同。
寫數(shù)據庫
將處理后的結構化數(shù)據對象持久化保存到數(shù)據庫,JOOQ提供了三種函數(shù),分別是insert、update、delete。修改記錄r,再更新到數(shù)據庫:
r.setValue(ORDERS.AMOUNT,r.getValue(ORDERS.AMOUNT).doubleValue()+100); r.update();
上面是單條記錄的更新。要注意的是,數(shù)據庫表必須有主鍵,自動生成的記錄類才會繼承UpdatableRecordImpl,只有繼承UpdatableRecordImpl的記錄類才支持update函數(shù)。
批量寫入數(shù)據庫是數(shù)據業(yè)務邏輯常見的場景,JOOQ也能實現(xiàn)。批量修改記錄集合T,并更新到數(shù)據庫:
R1.forEach(r->{ r.setValue(ORDERS.AMOUNT,r.getValue(ORDERS.AMOUNT).doubleValue()+100);}); R1.forEach(r->{ r.update();});
上面代碼循環(huán)記錄集合,手工更新每一條記錄,從而實現(xiàn)對整體集合的更新。可以看到,JOOQ通過硬寫代碼實現(xiàn)批量寫入,沒有進行封裝,很多時候不方便。如果一批記錄既有修改又有新增還有刪除,就必須區(qū)分三類記錄,再用不同的函數(shù)循環(huán)寫入,常見的辦法是繼承記錄類,新加一個“標識”屬性予以區(qū)分,或者保存一個未修改的原始記錄集合T,將修改后的集合NT與原始集合進行手工比對。無論哪種方法,手工實現(xiàn)的過程都很麻煩。
SPL對數(shù)據庫的寫入進行了封裝,只用一個update函數(shù)就實現(xiàn)單條和批量記錄的新增、修改、刪除,且支持混合更新。比如:原序表為T,經過增刪改一系列處理后的序表為NT,將變化結果持久化到數(shù)據庫的orders表:
conn.update(NT:T,orders)
訪問字段
JOOQ讀取單條記錄的Client字段:
R1.get(0).getClient(); R1.get(0).get(ORDERS.CLIENT);
上面代碼體現(xiàn)了JOOQ的核心優(yōu)勢:支持純粹的面向對象的字段訪問方式,不摻雜字符串、數(shù)字常量,或其他非Java的表達式,代碼風格高度統(tǒng)一。遺憾之處在于,上面代碼只適用于固定結構化數(shù)據對象。如果是查詢計算生成的動態(tài)記錄對象,就只能使用字符串字段名或數(shù)字序號訪問字段:
R2.get(0).get("Client"); R2.get(0).get(1);
動態(tài)記錄對象更加普遍,上面的字段訪問方式不算純粹的面向對象,代碼風格不一致,不支持自動補全,編寫時普遍麻煩。
SPL支持純粹的面向對象的字段訪問方式,不分固定或動態(tài),編寫時普遍方便:
T(1).Client
當然也支持字符串字段名或數(shù)字序號訪問字段:
T(1).field(2) T(1).field("Client")
SPL在面向對象方面更加純粹,風格更統(tǒng)一,編寫代碼更加方便。此外,SPL提供了很多JOOQ不支持的便利功能:默認字段名,可以用點號直接訪問,比如取第2個字段:T(1).#2;取多個字段,返回集合的集合:T.([Client,Amount])
有序訪問
有序訪問是業(yè)務邏輯開發(fā)的難點之一,JOOQ的記錄集合繼承自Java的有序集合ArrayList,具備一定的有序訪問能力,支持按下標取記錄和按區(qū)間取記錄:
R.get(3) R.subList(3,5);
再進一步的功能,就需要硬編碼實現(xiàn)了,比如后3條:
Collections.reverse(R); R.subList(0,3);
至于按位置集合取記錄、步進取記錄等功能,硬編碼就更麻煩了。
SPL序表同樣是有序集合,提供了順序相關的基本功能,比如按下標取、按區(qū)間?。?/p>
T(3) T.to(3,5)
序表是專業(yè)的結構化數(shù)據對象,許多順序相關的高級功能JOOQ Result沒有支持,序表則直接提供了,比如按倒數(shù)序號取記錄,可以直接用負號表示:
T.m(-3) //倒數(shù)第3條 T.m(to(-3,-5)) //倒數(shù)區(qū)間
再比如按位置集合取記錄、步進取記錄:
T.m(1,3,5,7:10) //序號是1、3、5、7-10的記錄 T.m(-1,-3,-5) //倒數(shù)第1,3,5條 T.step(2,1) //每2條取第1條(等價于奇數(shù)位置)
結構化數(shù)據計算
結構化數(shù)據計算能力是數(shù)據業(yè)務邏輯的核心功能,下面從簡單到復雜選取幾個常見題目,比較JOOQ和SPL的計算代碼。
改名
//等價的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的語法邏輯與SQL基本一致,可以達到用面向對象的方式模擬SQL的目的,這是JOOQ的重要優(yōu)點。相應的也有缺點,JOOQ的一項運算需要多個函數(shù)的組合才能實現(xiàn),每個函數(shù)都有自己的參數(shù)和語法規(guī)則,學習和編寫難度較大。此外,很多函數(shù)里的字段名必須附帶表名,即使單表計算也是如此,這說明JOOQ的語法不夠專業(yè),還有很大的改進空間。
SPL直接用面向對象的語法實現(xiàn)計算,一項運算對應一個函數(shù),引用字段不必附帶表名,語法更專業(yè),代碼更簡短。
條件查詢
//等價的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的條件表達式本身足夠簡單,JOOQ雖然在模擬SQL,但對條件表達式進行了過度封裝,函數(shù)數(shù)量過多,多層括號難閱讀,遠不如SQL好理解。SPL用一個函數(shù)實現(xiàn)條件查詢,條件表達式簡短易讀。
分組匯總
//等價的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ī)則很復雜,導致代碼過長。SPL直接用面向對象的語法,規(guī)則簡單,代碼更短。
前面都是較簡單計算,類似的計算還包括排序、去重、關聯(lián)、集合交并差等計算,這里不再一一列舉,總的來說,JOOQ進行簡單計算時比SQL和SPL代碼長,很多時候不易理解,開發(fā)效率較低。
各組前3名
//等價的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ù),代碼長度遠超SQL,語法也越來越不像SQL,編寫理解更加困難。SPL先對客戶分組,再求各組(即~)的前3名,最后合并各組計算結果,不僅代碼更簡短,且更易理解。
JOOQ使用了窗口函數(shù),只適合特定版本的數(shù)據庫,比如MySQL8,不能通用于其他版本的數(shù)據庫,要想在MySQL5下實現(xiàn)同樣的計算,代碼改動非常麻煩。SPL有獨立計算能力,代碼可通用于任何數(shù)據庫。
某支股票最大連續(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();
這個問題難度較高,需要綜合運用多種簡單計算。JOOQ很難直接表達連續(xù)上漲的概念,只能使用技巧變相實現(xiàn),即通過累計不漲天數(shù)來計算連續(xù)上漲天數(shù)。具體是,先按時間順序給每條記錄打漲跌標記risingflag,如果下跌,則標為1,如果上漲,則標為0;再按時間順序累計每條記錄的不漲天數(shù)norisingdays,只有當前記錄下跌時,這個數(shù)字才會變大,如果當前記錄上漲,則這個數(shù)字不變;再按不漲天數(shù)norisingdays分組,求各組記錄數(shù),顯然,連續(xù)下跌的一批記錄的norisingdays不同,每條記錄都會分到不同的組,該組計數(shù)為1,這個值不是解題目標,而連續(xù)上漲的一批記錄的norisingdays相同,可以分到同一組,該組計數(shù)即連續(xù)上漲的天數(shù),這個值是解題目標;最后用max函數(shù)求出最大的連續(xù)上漲天數(shù)。
JOOQ的編程過程是先寫SQL,再翻譯成JOOQ,對于簡單計算來說,SQL比較好寫,翻譯也不會太難,但對于本題這種綜合性計算來說,計算邏輯的技巧性比較強,SQL不好寫,翻譯的難度更大。此外,JOOQ表面上是方便調試的Java,但本質卻是SQL,和SQL一樣難以調試,這又為將來的維護工作埋下了大坑。
SPL代碼簡單多了:
APPL.sort(day).group@i(price<price[-1]).max(~.count())
這條SPL語句的計算邏輯和JOOQ是相同的,也是將連漲記錄分到同一組中再求最大的組成員數(shù),但表達起來要方便很多。group@i()表示遍歷序表,如果符合條件則開始新的一組(并使之前的記錄分到同一組),price<price[-1]這個條件表示股價下跌,則之前股價上漲的記錄會分到同一組。[-1]表示上一條,是相對位置的表示方法,price[-1]表示上一個交易日的股價,比整體移行(lag.over)更直觀。
相對位置屬于有序計算,SPL是專業(yè)的結構化計算語言,支持有序計算,代碼因此更簡單。除了有序集合,SPL還可以簡化多種復雜計算,包括多步驟計算、集合計算、復雜的關聯(lián)計算。相反,這幾類計算都是JOOQ不擅長的,通常要通過特殊技巧實現(xiàn),代碼很難寫。
SPL函數(shù)選項和層次參數(shù)
值得一提的是,為了進一步提高開發(fā)效率,SPL還提供了獨特的函數(shù)語法。有大量功能類似的函數(shù)時,JOOQ只能用不同的名字或者參數(shù)進行區(qū)分,使用不太方便。而SPL提供了非常獨特的函數(shù)選項,使功能相似的函數(shù)可以共用一個函數(shù)名,只用函數(shù)選項區(qū)分差別。比如,select函數(shù)的基本功能是過濾,如果只過濾出符合條件的第1條記錄,可使用選項@1:
T.select@1(Amount>1000)
對有序數(shù)據用二分法進行快速過濾,使用@b:
T.select@b(Amount>1000)
函數(shù)選項還可以組合搭配,比如:
Orders.select@1b(Amount>1000)
有些函數(shù)的參數(shù)很復雜,可能會分成多層。JOOQ對此并沒有特別的語法方案,只能拆成多個函數(shù)互相嵌套,盡力模擬成SQL語法,導致代碼冗長繁瑣。而SPL創(chuàng)造性地發(fā)明了層次參數(shù)簡化了復雜參數(shù)的表達,通過分號、逗號、冒號自高而低將參數(shù)分為三層。比如關聯(lián)兩個表:
join(Orders:o,SellerId ; Employees:e,EId)
流程處理
JOOQ支持部分存儲過程語法,包括循環(huán)語句和判斷語句,但這屬于商業(yè)版功能,且權限要求高、安全隱患大,難以移植,一般很少用到。除了存儲過程,JOOQ還可以利用Java實現(xiàn)流程處理,對數(shù)據庫沒有權限要求,安全隱患小,且可無縫移植。比如,根據規(guī)則計算獎金:
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ù)針對JOOQ的結構化數(shù)據對象進行了優(yōu)化,可以通過Lambda表達式簡化循環(huán)結構的定義,可以方便地處理集合對象的每個成員(代碼中的循環(huán)變量r)。forEach函數(shù)配合Lambda語法,整體代碼要比傳統(tǒng)循環(huán)語句簡單些。但也應該注意到,forEach函數(shù)里使用字段需要附帶循環(huán)變量名,對單表計算來說是多余的,同樣使用Lambda語法的SQL就可以省略變量名。此外,定義循環(huán)變量名也是多余的,SQL就不用定義。這些缺點都說明JOOQ在流程處理方面還不夠專業(yè),代碼還有很大的優(yōu)化空間。
SPL也有針對結構化數(shù)據對象進行優(yōu)化的循環(huán)函數(shù),直接用括號表示。同樣根據規(guī)則計算獎金:
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表達式,而且接口更簡單,不必定義循環(huán)變量,使用字段時不必引用變量名,比JOOQ更方便,專業(yè)性也更強。除了循環(huán)函數(shù),SPL還有更多專業(yè)的流程處理功能,比如:每輪循環(huán)取一批而不是一條記錄;某字段值變化時循環(huán)一輪。
SPL專業(yè)的流程處理功能,配合專業(yè)的結構化數(shù)據對象和結構化數(shù)據計算能力,可大幅提高數(shù)據業(yè)務邏輯的開發(fā)效率。一個完整的例子:計算出獎金,并向數(shù)據庫插入新記錄。JOOQ需要生成多個文件,編寫大段代碼才能實現(xiàn),SPL就簡單多了:
A | B | C | |
---|---|---|---|
1 | =db=connect@e("dbName") | /連接數(shù)據庫,開啟事務 | |
2 | =db.query@1("select sum(Amount) from sales where sellerID=? and year(OrderDate)=? and month(OrderDate)=?", p_SellerID,year(now()),month(now())) | /查詢當月銷售額 | |
3 | =if(A2>=10000 :200, A2<10000 && A2>=2000 :100, 0) | /本月累計獎金 | |
4 | =p_Amount*0.05 | /本單固定獎金 | |
5 | =BONUS=A3+A4 | /總獎金 | |
6 | =create(ORDERID,CLIENT,SELLERID,AMOUNT,BONUS,ORDERDATE) | /創(chuàng)建訂單的數(shù)據結構 | |
7 | =A6.record([p_OrderID,p_Client,p_SellerID,p_Amount,BONUS,date(now())]) | /生成一條訂單記錄 | |
8 | >db.update@ik(A7,sales;ORDERID) | /嘗試寫入庫表 | |
9 | =db.error() | /入庫結果 | |
10 | if A9==0 | >A1.commit() | /成功,則提交事務 |
11 | else | >A1.rollback() | /失敗,則回滾事務 |
12 | >db.close() | /關閉數(shù)據庫連接 | |
13 | return A9 | /返回入庫結果 |
利用SPL的流程處理語句,可以實現(xiàn)存儲過程的所有能力,包括游標的循環(huán)和判斷。SPL不依賴數(shù)據庫,不需要數(shù)據庫權限,沒有安全隱患,相當于庫外的存儲過程,同時,這些功能也都是開源的。
應用結構
Java集成
JOOQ本身就是Java,可被其他Java代碼直接調用。
SPL是基于JVM的數(shù)據計算語言,提供了易用的JDBC接口,可被JAVA代碼無縫集成。比如,將業(yè)務邏輯代碼存為腳本文件,在JAVA中以存儲過程的形式調用文件名:
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)是編譯型語言,不支持熱部署,修改代碼后需要重新編譯并重啟整個應用,加大了維護難度,降低了系統(tǒng)穩(wěn)定性。
SPL是解釋型語言,代碼以腳本文件的形式外置于JAVA,支持熱部署,修改后不必編譯,也不必重啟應用。由于SPL代碼不依賴JAVA,業(yè)務邏輯和前端代碼物理分離,耦合性也更低。
代碼移植
JOOQ的部分代碼可以移植,比不可移植的MyBatis方便。比如業(yè)務邏輯中常用于分頁的limit(M).offset(N),在Oracle11g數(shù)據庫下會被翻譯為rownum子查詢;如果數(shù)據庫改為MSSQL2012,只要重新生成并部署實體類,不必修改業(yè)務邏輯,同樣的代碼就會翻譯成offset next語句。
能夠移植的代碼畢竟是少數(shù),大部分JOOQ代碼都是不可以移植的,比如前面例子里的窗口函數(shù)。移植時需要讀懂原JOOQ代碼,反翻譯成原SQL,再改成新SQL,最后翻譯成新JOOQ代碼,過程較繁難度較高。業(yè)務邏輯普遍具有復雜性,移植工作就更難了。
SPL具有獨立計算能力,不必借用SQL,憑借豐富的內置函數(shù)庫就能實現(xiàn)復雜的結構化數(shù)據計算,計算代碼可在數(shù)據庫間無縫移植。在數(shù)據庫取數(shù)代碼中,SPL也要執(zhí)行方言SQL生成序表,雖然取數(shù)SQL比較簡單,手工移植不難,但仍有一定工作量,為了使取數(shù)代碼便于移植,SPL專門提供了不依賴特定數(shù)據庫的通用SQL,可在主流數(shù)據庫間無縫移植。
通過多方面的比較可知:JOOQ可以進行較簡單的查詢統(tǒng)計,但對于較復雜的業(yè)務邏輯開發(fā)就顯得比較繁瑣,尤其是有序計算、多步驟計算、集合計算、復雜的關聯(lián)查詢,存在翻譯SQL的工作量大,代碼冗長,難以修改,難以移植等問題。SPL語法簡練、表達效率高、代碼移植方便,結構化數(shù)據對象更專業(yè),函數(shù)更豐富且計算能力更強,流程處理更方便,開發(fā)效率遠高于JOOQ。
以上就是Java 下數(shù)據業(yè)務邏輯開發(fā)技術 JOOQ 和 SPL的詳細內容,更多關于Java數(shù)據業(yè)務邏輯JOOQ SPL的資料請關注腳本之家其它相關文章!
相關文章
java字符串數(shù)組進行大小排序的簡單實現(xiàn)
下面小編就為大家?guī)硪黄猨ava字符串數(shù)組進行大小排序的簡單實現(xiàn)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09SpringCloud微服務開發(fā)基于RocketMQ實現(xiàn)分布式事務管理詳解
分布式事務是在微服務開發(fā)中經常會遇到的一個問題,之前的文章中我們已經實現(xiàn)了利用Seata來實現(xiàn)強一致性事務,其實還有一種廣為人知的方案就是利用消息隊列來實現(xiàn)分布式事務,保證數(shù)據的最終一致性,也就是我們常說的柔性事務2022-09-09詳解java JDK 動態(tài)代理類分析(java.lang.reflect.Proxy)
這篇文章主要介紹了詳解java JDK 動態(tài)代理類分析(java.lang.reflect.Proxy)的相關資料,需要的朋友可以參考下2017-06-06Java并發(fā)系列之CountDownLatch源碼分析
這篇文章主要為大家詳細介紹了Java并發(fā)系列之CountDownLatch源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03