江日念, 許 錕, 林 霞
(中國石油勘探開發(fā)研究院 計算機應用技術(shù)研究所,北京 100083)
數(shù)據(jù)列表(報表)作為一種數(shù)據(jù)集中展示方式,在信息系統(tǒng)中充當著重要的角色,是系統(tǒng)重要的組成部分. 用戶的需求有很大部分來源于對數(shù)據(jù)展示的需求.傳統(tǒng)的開發(fā)方式是逐一為數(shù)據(jù)列表編碼,為滿足特定需求逐一開發(fā)界面,開發(fā)出來的是靜態(tài)數(shù)據(jù)列表. 靜態(tài)數(shù)據(jù)列表存在以下3方面的缺點.
(1) 可重用性低,開發(fā)效率低. 即便列表高度相似,也必須單獨實現(xiàn),代碼、界面無法重用,開發(fā)人員把大量時間浪費在相似代碼的編寫、調(diào)試上,無法專注于核心業(yè)務邏輯的設(shè)計與實現(xiàn),導致開發(fā)效率不高.
(2) 可擴展性低,部署能力弱. 用戶在系統(tǒng)上線使用后,往往都會有對列表展示、列表查詢變更的需求,此時要進行調(diào)整就比較麻煩,必須由開發(fā)人員修改源碼,甚至重新發(fā)布系統(tǒng).
(3) 開發(fā)門檻高,參與程度低. 傳統(tǒng)方式要求人員具備一定的開發(fā)能力,所以在開發(fā)和后期運維調(diào)整過程中,只能全程由開發(fā)人員參與,將業(yè)務人員隔絕在開發(fā)之外,不能充分參與開發(fā)與運維,導致人力資源的浪費.
為了解決數(shù)據(jù)列表中的上述問題,陳傳波等提出一種Web報表模型,通過Crystal Reports引擎和ADO.NET對報表底層接口進行控制,實現(xiàn)報表自定義[1]. 金雨等人把報表分解為若干結(jié)構(gòu)單元,建立了通用報表模型,并在Dephi開發(fā)環(huán)境下闡述了其設(shè)計和研發(fā)過程[2]. 為了進一步提升報表的柔性,部分研究的工作基于XML來展開. 高紅艷通過一種可視化的報表設(shè)計器來解決報表樣式復雜的問題,并提出一種基于XML的無插件方案來解決報表數(shù)據(jù)問題[3]. 盧笑天等設(shè)計了基于XML的可定制查詢報表系統(tǒng),滿足報表查詢條件和查詢字段的動態(tài)可定制,同時提供了可拖動可視化的定制界面[4]. 符云清等利用Excel設(shè)計自定義報表,程序讀取上傳的Excel并解析為HTML文件和XML文件,系統(tǒng)根據(jù)Excel中配置的字段生成SQL,實現(xiàn)報表格式和數(shù)據(jù)分離[5]. 湯加等利用XML實現(xiàn)動態(tài)創(chuàng)建數(shù)據(jù)表,并在此基礎(chǔ)上配置數(shù)據(jù)源及其字段與報表單元格之間的關(guān)系,系統(tǒng)將據(jù)此自動拼裝出相應的SQL,從而快速生成各類基于單數(shù)據(jù)源的報表[6].
上述的部分文獻通過XML實現(xiàn)報表的靈活定制,但從開發(fā)者和用戶的角度看,尚不能完全滿足以下四個數(shù)據(jù)列表典型需求:
(1) 組合查詢定制
開發(fā)者可以很方便地增加或者修改查詢條件,而盡可能少的修改系統(tǒng),最好不要修改后臺代碼,從而減少因重新部署導致系統(tǒng)停機風險,同時降低開發(fā)工作量,提升效率,這方面已經(jīng)有許多相關(guān)工作[7,8].
(2) 數(shù)據(jù)列自定義
用戶可以對不關(guān)注的數(shù)據(jù)列設(shè)置隱藏,尤其在數(shù)據(jù)列過多的情況下,通過自定義列的顯示與隱藏,突顯用戶關(guān)注的信息,屏蔽不重要信息,提升用戶體驗.
(3) 數(shù)據(jù)列渲染
一個功能完備的數(shù)據(jù)列表,不局限于數(shù)據(jù)的展示和查詢,還應該具備交互功能,比如按鈕列、選擇列、超鏈接列、狀態(tài)列等,這些列需要通過表格組件的渲染將原始數(shù)據(jù)“翻譯”并呈現(xiàn)在用戶面前,為用戶提供交互功能. 對開發(fā)者來說,可配置的列渲染大大節(jié)省數(shù)據(jù)列表開發(fā)工作量.
(4) 數(shù)據(jù)范圍控制
對擁有不同權(quán)限的用戶來說,同一個數(shù)據(jù)列表應該有不同的數(shù)據(jù)范圍,以人員管理為例:公司總裁可以查詢公司所有人員信息,部門經(jīng)理只能查詢本部門人員信息.
針對傳統(tǒng)靜態(tài)數(shù)據(jù)列表的不足,本文提出一種動態(tài)數(shù)據(jù)查詢技術(shù),以滿足信息系統(tǒng)動態(tài)數(shù)據(jù)列表需求.
XML是一種數(shù)據(jù)交換格式,它以一種開放的自我描述方式定義數(shù)據(jù)結(jié)構(gòu),在描述數(shù)據(jù)內(nèi)容的同時能突出對結(jié)構(gòu)的描述,從而體現(xiàn)數(shù)據(jù)之間的關(guān)系. XML具有擴展性好、結(jié)構(gòu)性好、與平臺無關(guān)的特點,它提供了統(tǒng)一的方法來描述和交換獨立于應用程序或供應商的結(jié)構(gòu)化數(shù)據(jù).
之所以選擇XML配置數(shù)據(jù)列表正是因為其靈活、可定制的結(jié)構(gòu)性和自描述性. 其結(jié)構(gòu)性可以顯著降低數(shù)據(jù)列表配置的門檻,能夠使得管理人員和業(yè)務人員都可以參與到數(shù)據(jù)列表的配置開發(fā). 同時,XML的自描述性能夠讓其既作為數(shù)據(jù)源,又可以作為一種設(shè)計文檔,它正好彌補了傳統(tǒng)設(shè)計文檔和實現(xiàn)之間不一致性,因為XML所做的修改會及時反映到實現(xiàn)中,時刻保持著與實現(xiàn)同步. 其作為模型存儲、數(shù)據(jù)存儲的文件,已經(jīng)得到了廣泛的應用[9-13].
Digester是Apache的一個組件,Digester包可以配置一個從XML文件到Java對象映射[14]. 其底層采用SAX來解析XML文件. Digester維持了一個對象棧,可以看作對象轉(zhuǎn)換平臺,用來存放轉(zhuǎn)換中生成的、或是為轉(zhuǎn)換臨時創(chuàng)建的Java對象. 由于Digester屏蔽了底層實現(xiàn)細節(jié),使用者只需關(guān)注操作本身,大大簡化了轉(zhuǎn)換操作. 使用Digester的注解模式,通過建立與XML內(nèi)容相互映射的JavaBean來解析XML. 為了簡化使用,它通過匹配模式來定位要解析的XML標簽.示例如下:
每個標簽與相應的匹配模式對應如表1 (僅列出部分):
表1 標簽匹配模式
如果將XML文件結(jié)構(gòu)視為一棵樹的話,那么每個標簽的匹配模式就是從根元素到這個元素的路徑,除了使用具體的標簽,還可以使用通配符.
使用匹配模式可以很方便地定位需要處理的元素,規(guī)則在匹配模式被找到時起作用. 所有的規(guī)則都是從org.apache.commons.digester.Rule派生的. 可通過@ObjectCreate、@SetProperty、@BeanProperty-Setter等注解和Digester解析功能就可以將節(jié)點queryContext,query,column分別映射到類Query-Context,Query,Column上.
dhtmlxGrid是一個靈活、智能、容易使用的JavaScript表格控件,它允許使用者以Ajax交互方式實現(xiàn)表格,使其具有單元格編輯、固定多表頭、固定多表尾、列寬可變、列可排序、列可拖動、凍結(jié)列等功能. 其豐富的功能足以滿足用戶對數(shù)據(jù)列表的各種展示需求.
dhtmlxGrid控件支持XML、JSON,官方網(wǎng)站提供了詳細的英文API文檔. 為了更好、更方便地在動態(tài)數(shù)據(jù)查詢技術(shù)中應用dhtmlxGrid控件,本文使用已改進的dhtmlxGrid控件,并對其進行二次封裝,二次封裝的控件以下簡稱CommonGrid.
動態(tài)數(shù)據(jù)查詢的設(shè)計理念是:將數(shù)據(jù)列表抽象化,分為表級別配置與列級別配置,并以XML保存該配置;系統(tǒng)初始化后加Apache Digester讀取XML配置并對象化緩存到內(nèi)存中,緩存時分為Product模式和Debug模型,Product模式不會監(jiān)聽XML配置的變化,而Debug模式將動態(tài)監(jiān)聽XML配置并及時更新緩存.后端動態(tài)查詢引擎根據(jù)前端發(fā)送請求,動態(tài)獲取配置和數(shù)據(jù),返回到前端,前端CommonGrid組件對數(shù)據(jù)進行展示和渲染,總體框架如圖1.
圖1 動態(tài)數(shù)據(jù)查詢框架
動態(tài)數(shù)據(jù)查詢框架基于Spring Boot實現(xiàn),分為Web層、業(yè)務邏輯層、數(shù)據(jù)訪問層、實體層,各層之間相互銜接,且明確地完成了自身職責. ① 前端頁面創(chuàng)建CommonGrid表格控件,加載查詢條件,向服務器發(fā)送數(shù)據(jù)加載、查詢請求,Web獲取請求參數(shù)并查詢緩存的XML配置. ② 調(diào)用業(yè)務邏輯層的動態(tài)查詢引擎處理,進行配置解析,將配置解析成可執(zhí)行的查詢操作. ③ 調(diào)用持久化方法,執(zhí)行查詢操作. ④ 查詢結(jié)果映射到JavaBean對象. ⑤ 服務器處理完請求,將數(shù)據(jù)、分頁、列等信息以JSON格式傳送前端CommonGrid處理.
一個基本的數(shù)據(jù)列表是由數(shù)據(jù)列、數(shù)據(jù)行組成的.同時,數(shù)據(jù)列表還附帶著一些功能,比如列表分頁、數(shù)據(jù)查詢、數(shù)據(jù)導出、合并行、合并列、多表頭、多表尾、拖拽列等. 通過對數(shù)據(jù)列表的分析和抽象,以及結(jié)合CommonGrid的特點,構(gòu)建數(shù)據(jù)列表模型如圖2所示. 其模型的構(gòu)建步驟如下:
(1) 列表抽象:是對整個列表的整體抽象,對應的是Query類,用于配置列表的全局屬性. 其屬性包括表的ID、表名、初始化分頁信息(是否允許分頁、每頁記錄數(shù))、凍結(jié)列數(shù)、初始化排序信息. 為了獲取數(shù)據(jù),根據(jù)不同的數(shù)據(jù)獲取方式,需要配置對應的SQL或者映射數(shù)據(jù)指向的className. 列表中也配置了些冗余屬性,如用于緩存數(shù)據(jù)列信息的columnList,用于緩存調(diào)用方法信息的callList等.
(2) 數(shù)據(jù)列抽象:對數(shù)據(jù)列的抽象,對應Column類,用于配置列屬性,是數(shù)據(jù)列表中非常重要的部分.前端組件CommonGrid根據(jù)數(shù)據(jù)列配置動態(tài)渲染列,其屬性配置如表2所示.
圖2 數(shù)據(jù)列表模型(XML結(jié)構(gòu)抽象)
表2 數(shù)據(jù)列Column的屬性
(3) 數(shù)據(jù)行抽象:從后臺獲取的數(shù)據(jù)行抽象,對應Row類,包括兩個屬性:行ID和對象數(shù)組. 數(shù)據(jù)行的集合構(gòu)成了數(shù)據(jù)列表的數(shù)據(jù),并以JSON格式返回給前端組件.
(4) 執(zhí)行方法抽象:為了靈活定制表格組件在初始化前、初始化中、初始化后等不同的時間節(jié)點要執(zhí)行的前端方法,定義了BeforeCall、Call、AfterCall類,類提供了方法名和參數(shù)兩個屬性,CommonGrid組件在對應時間節(jié)點獲取這些在后臺組裝好BeforeInit、CallList、AfterInit的的鉤子方法列表并執(zhí)行.
(5) 查詢條件抽象:用于接收前端查詢交互時的信息,對應QueryCondition類,包括的信息有分頁信息pageInfo、查詢條件信息contionMap、查詢配置主鍵queryId等. 在數(shù)據(jù)列表首次加載時,從后端緩存的XML配置中獲取信息并組裝. 非首次查詢時,由前端實時參數(shù)和前端緩存中獲取,如果兩者均存在,優(yōu)先從前端實時參數(shù)中獲取,并更新到前端緩存. 數(shù)據(jù)列表的其他需要交互的實時參數(shù),如通用導出的Excel表信息也可以在QueryCondtion類中定義,如表名sheetName,表頭標題sheetTitle.
對前端而言,動態(tài)查詢引擎就是個黑盒,只要接收參數(shù),動態(tài)查詢引擎就以一定的格式向客戶端輸出滿足條件的JSON數(shù)據(jù). 圖3為其工作流程示意圖.
圖3 動態(tài)查詢引擎工作流程
動態(tài)查詢引擎支持三種查詢方式:SQL、HQL、接口. SQL查詢用于對多表關(guān)聯(lián)的復雜查詢,HQL常用于單表對象化查詢,接口查詢是在SQL和HQL無法滿足需求情況下推薦的方式,比如條件參數(shù)的獲取需要復雜的業(yè)務邏輯計算才能得到. 這三種方式都需要查詢條件、分頁信息、排序信息作為輸入、查詢數(shù)據(jù)和配置作為輸出. 在界面首次加載時,分頁信息和排序信息從XML配置中讀取,而當用戶發(fā)送查詢請求、換頁請求、排序請求時,分頁信息和排序信息由客戶端傳參而來.
下面以SQL查詢?yōu)槔f明動態(tài)查詢引擎的具體工作過程:
(1) 組裝分頁信息:如果客戶端傳入分頁信息,則優(yōu)先讀取客戶端分頁信息,否則讀取XML配置中的分頁信息;
(2) 組裝查詢條件:獲取客戶端傳入的查詢條件,逐一遍歷查詢條件,若傳入值為空,則跳過. 否則將獲取的查詢條件名、查詢操作符(between、or、like、in、eq、not_eq等)、傳入值在引擎中組裝,直至遍歷結(jié)束;
(3) 組裝排序信息:如果客戶端傳入排序信息,則讀取客戶端排序信息,否則讀取XML配置的排序信息,并將排序組裝到SQL中;
(4) 組裝數(shù)據(jù)范圍條件:在權(quán)限管理中,獲取用戶對列表的授權(quán)配置,并組裝到SQL中;
(5) 查詢執(zhí)行:組裝的兩個查詢SQL,一個是獲取數(shù)據(jù)的SQL,傳入的參數(shù)為查詢條件,分頁信息; 一個是獲取數(shù)據(jù)總記錄數(shù)的SQL,傳入?yún)?shù)為查詢條件;
(6) 數(shù)據(jù)對象化、格式化:通過反射將數(shù)據(jù)映射到JavaBean對象,并按照XML配置格式化,生成CommonGrid控件能夠接收的JSON格式的數(shù)據(jù)列表;
(7) 拼接列屬性:形成CommonGrid要執(zhí)行的方法列表. 將各列的配置屬性,按照方法名和參數(shù)拼接起來,形成CommonGrid可執(zhí)行的方法名和列表;
(8) 數(shù)據(jù)渲染與展示:將分頁信息、數(shù)據(jù)列表、CommonGrid執(zhí)行方法列表以JSON格式發(fā)送給客戶端.
基于該動態(tài)查詢引擎實現(xiàn)的動態(tài)列表實現(xiàn)了組合查詢定制、數(shù)據(jù)列自定義、數(shù)據(jù)列渲染、數(shù)據(jù)權(quán)限控制等常見用戶自定義數(shù)據(jù)列表需求,下面以人事信息系統(tǒng)為例,說明其應用效果.
需求:針對用戶對數(shù)據(jù)列表查詢條件頻繁變更的需求,動態(tài)查詢引擎僅需要在前端頁面添加或者修改相應的控件,而無須修改后端代碼,也無須對系統(tǒng)進行重啟.
實現(xiàn):將查詢條件配置在查詢框中,也可以配置在列頭,查詢引擎解析查詢條件,自動獲取并展示. 如圖4所示.
圖4 組合查詢定制(人事系統(tǒng))
需求:用戶可自定義數(shù)據(jù)列的顯示與隱藏,只顯示自己關(guān)注的列.
實現(xiàn):如圖5所示,用戶使用拖拽自定義列的顯示與隱藏并保存到數(shù)據(jù)庫中,查詢引擎將XML配置與用戶自定義進行比對,從而決定顯示哪些數(shù)據(jù)列.
圖5 自定義數(shù)據(jù)列
需求:用戶需要和數(shù)據(jù)列表進行交互,比如按鈕列、選擇列等,同時需要將部分原始數(shù)據(jù)翻譯成用戶可看懂的列,比如將人員狀態(tài)字段的“0”、“1”翻譯成對應的“在職”、“離職”,對長文本而言,還需要截斷,在鼠標懸停時顯示全部文本等.
實現(xiàn):在XML配置列通過render屬性或者fnRender配置回調(diào)函數(shù),支持以下類型:
(1) render (type=eq) 固定值的翻譯;
(2) render (type=window) 彈出窗體;
(3) render (type=link) 超鏈接;
(4) tooltip 鼠標懸停提示;
(5) fnRender 渲染回調(diào)函數(shù). 前端CommonGrid解析配置,按需進行數(shù)據(jù)渲染.
需求:不同權(quán)限的用戶針對同一數(shù)據(jù)列表擁有不同的數(shù)據(jù)范圍,通過系統(tǒng)配置實現(xiàn),避免類似的代碼開發(fā)多次,這樣可以提升系統(tǒng)可維護性.
實現(xiàn):在系統(tǒng)后臺的權(quán)限管理中動態(tài)配置數(shù)據(jù)權(quán)限,查詢引擎解析配置,組合到查詢條件中,形成對應權(quán)限的數(shù)據(jù)范圍,如圖6所示.
圖6 配置數(shù)據(jù)范圍
除以上比較典型的功能外,動態(tài)查詢還集成了通用導出、凍結(jié)列、自定義多表頭等.
本文在Spring Boot框架下提出一個動態(tài)數(shù)據(jù)查詢技術(shù),從技術(shù)實現(xiàn)的總體框架、數(shù)據(jù)列表抽象與解析、動態(tài)查詢引擎三個方面闡述了其設(shè)計和實現(xiàn)細節(jié).并從開發(fā)者和用戶對動態(tài)數(shù)據(jù)列表的典型需求考慮,選擇組合查詢定制、數(shù)據(jù)列自定義、數(shù)據(jù)列渲染、數(shù)據(jù)范圍控制展示了系統(tǒng)的實現(xiàn)效果. 該技術(shù)已經(jīng)應用于某機構(gòu)的多個系統(tǒng)中,在人事系統(tǒng)中的應用效果證明,它能極大地提高開發(fā)效率、降低開發(fā)門檻、提高數(shù)據(jù)列表的擴展性和可維護性. 該技術(shù)具有一定的推廣性和實用性,在企業(yè)信息系統(tǒng)研發(fā)中,進一步結(jié)合代碼生成器使用,只需編寫一個實體即可快速完成一個數(shù)據(jù)列表的增刪改查功能,且可以靈活擴展.