oql4j物件查詢語言

奕辰杰發表於2024-09-11

一、簡介

1.1 介紹

oql4j 全稱 Object Query Language For Java,即java版本的物件查詢語言,支援使用編寫查詢語言快速的搜尋出符合條件的物件,簡化物件的過濾處理操作。
目前支援where條件的過濾和select的對映查詢,後續將支援update更新等操作。

二、使用指南

2.1 依賴說明

  • 基於jdk8版本
  • 依賴antlr4包(pom依賴自動引入)
  • 依賴commons-beanutils包(pom依賴自動引入)

2.2 使用主程式碼

OqlClientBuilder oqlClientBuilder = new OqlClientBuilder();
OqlClient oqlClient = oqlClientBuilder.build();

然後就可以使用oqlClient物件,進行各種where條件的操作,select的對映查詢,例如操作oqlClient.doWhereFilter(...)、oqlClient.doSelect(...)

OqlClient介面的預設實現類是DefaultOqlClientImpl,此類是執行緒安全的,所以oqlClient物件可作為單例在整個專案中使用

2.3 Where條件使用案例的demo

OqlClientBuilder oqlClientBuilder = new OqlClientBuilder();
OqlClient oqlClient = oqlClientBuilder.build();
// 原資料
Map<String, String> srcMap = new HashMap<>();
srcMap.put("name", "李明");
// 編寫oql語法
String whereOqlExp = " ${name} is not null ";
// name欄位不為空,判斷是否存在這樣的原資料。
boolean result = oqlClient.doWhereExists(whereOqlExp, srcMap);
// 列印的結果為true
System.out.println(result);

更多的使用demo,可參考程式碼的測試用例:com.cat.oqj4j.core.OqlClientlWhereTest

2.4 Select對映使用案例的demo

OqlClientBuilder oqlClientBuilder = new OqlClientBuilder();
OqlClient oqlClient = oqlClientBuilder.build();
// 原資料
PersonTest p01 = new PersonTest();
p01.setName("張三");
p01.setIsMan(true);
p01.setAge(36);
// 編寫oql語法
String selectOqlExp = " ${age} myAge, ${isMan} as knownGender, ${name} as myName, ${addr.province} as  myAddr.province,  ${addr.city} myAddr.city";
// 執行select對映,將對映結果賦值到MyPersonTest物件。
MyPersonTest result = oqlClient.doSelect(selectOqlExp, p01, MyPersonTest.class);

更多的使用demo,可參考程式碼的測試用例:com.cat.oqj4j.core.OqlClientlSelectTest

三、語法規則

3.1 語法基礎

3.1.1 基本型別說明:

型別 型別demo
字串 有單雙引號的內容都是屬於字串,例如 "name"、'name' 表示常量name
數值 數字類的都屬於數值型別,包括整數和小數以及科學計數法,例如99、9993.66、-321、 1e3
布林 布林值型別,只有真和假,並且不區分大小寫,例如true、True、TRUE、false、False、FALSE
null null型別,並且不區分大小寫,例如null、Null、NULL

3.1.2 語法概覽:

關鍵字 說明 語法demo 詳細說明
${} 取值操作 ${name} 表示取物件的name屬性 3.1.2.1 取值操作
F{} 執行函式 F{StrLen(${name})} 表示取出物件的name屬性值並且執行StrLen函式 3.1.2.2 執行函式

3.1.2.1 取值操作

  • 使用${}來表示,例如${name} 表示取物件的name屬性值,支援巢狀取值,使用.來分割,
    例如${addr.city}表示取物件的addr屬性下的city屬性,即取出addr屬性後,再操作addr物件取出city屬性。


  • 注意巢狀取值時,如果上層屬性必為null,則直接返回null值,例如巢狀取city屬性時,city的上層屬性addr為null,則取到的city屬性也為null。


  • 取值操作是基本語法,支援在select對映語法、where條件語法中使用。

3.1.2.2 執行函式

  • 用於執行預定義和自定義的函式,使用F{}關鍵字,關鍵字不區分大小寫, F{}、f{} 都能識別。


  • 格式為F{funName}、F{funName()}、F{funName(arg1,arg2...)} 均支援,預設都會傳操作集合的原物件和遍歷的當前物件這兩個引數,
    有需要的可以額外追加引數,並且追加的引數可以為常量引數也可以為動態取值引數。
    例如: F{StrLen(${name})} 表示取出物件的name屬性值作為引數,執行StrLen函式。
    例如: F{CastInt(F{StrLen(${age})})} > 2 表示取出物件的age屬性值,取得字串長度,然後再轉換為int型別,並對函式結果進行大於2判斷。


  • 取值操作是基本語法,支援在select對映語法、where條件語法中使用。
    例如在select對映語法的使用: F{IfNull(${name}, '未知')}
    例如在where條件語法的使用: F{StrLen(${name})} > 2


  • 框架預定義的函式如下(支援自定義函式,具體可檢視 5.1 自定義函式):

    函式名 功能 所需引數 詳細說明
    StrLen 字串長度 StrLen(arg1),arg1:用於判斷字串長度的引數 將傳入的引數轉換為字串,統計並返回字串長度。如果引數為null,則返回長度為0。
    If 三元表示式條件處理 If(arg1,arg2,arg3),arg1:用於判斷條件的真假,arg2:條件為真時的返回值,arg3:條件為假時的返回值 用於判斷arg1引數,如果為真則返回arg2引數,反之返回arg3。數值0和false字串都屬於假,1和true字串則屬於真(false和true字串不區分大小寫)。
    IfNull null結果時取預設值 IfNull(arg1,arg2),arg1:主引數值,arg2:預設值 如果arg1引數不為null則返回此值,否則返回arg2引數。
    CastStr 轉換為字串 CastStr(arg1),arg1:用於轉換為字串的引數 將傳入的引數轉換為字串,如果引數為null,則返回null。
    CastBigDec 轉換為BigDecimal CastBigDec(arg1),arg1:用於轉換為BigDecimal的引數 將傳入的引數轉換為BigDecimal,如果引數為null,則返回null。
    CastInt 轉換為Integer CastInt(arg1),arg1:用於轉換為Integer的引數 將傳入的引數轉換為Integer,如果引數為null,則返回null。
    CastLong 轉換為Long CastLong(arg1),arg1:用於轉換為Long的引數 將傳入的引數轉換為Long,如果引數為null,則返回null。
    CastByte 轉換為Byte CastByte(arg1),arg1:用於轉換為Byte的引數 將傳入的引數轉換為Byte,如果引數為null,則返回null。
    CastFloat 轉換為Float CastFloat(arg1),arg1:用於轉換為Float的引數 將傳入的引數轉換為Float,如果引數為null,則返回null。
    CastDouble 轉換為Double CastDouble(arg1),arg1:用於轉換為Double的引數 將傳入的引數轉換為Double,如果引數為null,則返回null。
    CastChar 轉換為Character CastChar(arg1),arg1:用於轉換為Character的引數 將傳入的引數轉換為Character,如果引數為null或者為""字串,則返回null,長度大於1則轉換報錯。
    CastBool 轉換為Boolean CastBool(arg1),arg1:用於轉換為Boolean的引數 將傳入的引數轉換為Boolean,如果引數為null,則返回null。數值0和false字串都屬於假,1和true字串則屬於真(false和true字串不區分大小寫)。
    ClassSimpleName 獲取Class簡短名稱 ClassSimpleName(arg1),arg1:用於獲取Class位元組碼物件的簡短名稱的引數 將傳入的引數去除Class位元組碼物件並獲取簡短名稱,如果引數為null,則返回"null"字串。
    NotExistNull 判斷全部引數都不為null NotExistNull(arg1,arg2,arg3...),需要判斷的引數,不限制引數數量 將傳入的引數逐一判斷,如果都不為null則返回true,反之false。如果沒有傳入任何引數,則返回true。
  • 框架預定義的函式使用demo,可參考程式碼的測試用例:com.cat.oqj4j.core.OqlClientFunTest

3.2 Where條件語法

3.2.1 語法概覽:

關鍵字 說明 語法demo 詳細說明
and 並且條件 ${name} = '張三' and ${age} > 20 表示名稱為張三並年齡大於20 3.2.1.1 並且條件
or 或者條件 ${name} = '張三' or ${name} = '李四' 表示名稱為張三或者李四 3.2.1.2 或者條件
() 括號運算 (${name} = '張三' or ${name} = '李四') and ${age} > 20 表示先判斷名稱可為張三或者李四,然後再判斷年齡大於20歲 3.2.1.3 括號運算
in 同時指定多個或者條件 ${name} in ('張三','李四') 表示名稱為張三或者李四 3.2.1.4 同時指定多個或者條件
between 範圍條件 ${age} between 20 and 30 表示年齡在[20,30]範圍內 3.2.1.5 範圍條件
is null 等於空 ${name} is null 表示姓名為null 等價於 ${name} = null 3.2.1.6 等於空
is not null 不等於空 ${name} is not null 表示姓名不為null 等價於 ${name} != null 3.2.1.7 不等於空

3.2.1.1 並且條件

  • 對多個條件進行交集處理,and關鍵字不區分大小寫,即and、And、AND 都能識別。
  • 支援用 && 代替and關鍵字。例如:${name} = '張三' and ${age} > 20 等價於 ${name} = '張三' && ${age} > 20
  • and的優先順序比or要高。

3.2.1.2 或者條件

  • 對多個條件進行並集處理,or關鍵字不區分大小寫,即or、Or、OR 都能識別。
  • 支援用 || 代替or關鍵字。例如:${name} = '張三' or ${age} > 20 等價於 ${name} = '張三' || ${age} > 20
  • or的優先順序小於and。

3.2.1.3 括號運算

  • 用於提升運算優先順序,使用()將表示式括起來,括號內的表示式將優先執行,常用於在and、or條件下進行配套使用。
  • 括號表示式的運算優先順序比and、or都高

3.2.1.4 同時指定多個或者條件

  • 當存在or引數時,可以使用in關鍵字來實現,關鍵字不區分大小寫,即in、In、IN都能識別。
  • 格式為 keyName in (arg1,arg2,arg3,arg4...),arg引數可以是常量引數,也可以是動態取值引數,
    例如 ${name} in ('李白', ${formerName}) 表示判斷物件的name屬性值是否為 李白 或者為此物件的formerName屬性。
  • in內的arg引數不限制數量,並且可以同時為各種型別資料,例如可以同時有字串型別、數值型別等。

3.2.1.5 範圍條件

  • 當需要判斷範圍時,可以使用between關鍵字,關鍵字不區分大小寫,即between、Between、BETWEEN等都能識別。
  • 格式為keyName between arg1 and arg2、keyName between arg1 && arg2, 大於等於arg1 並且小於等於 arg2,等價於 >=arg1 and <=arg2。
    例如 ${age} between 20 and 30 表示年齡在[20,30]範圍內,20、21、29、30等均符合,用&&寫法則為${age} between 20 && 30

3.2.1.6 等於空

  • 用於判斷值是否為null,使用 is null 關鍵字,關鍵字不區分大小寫, is、IS、null、Null、NULL等都能識別。
  • 格式為 keyName is null, 例如 ${name} is null 表示name屬性值為null的判斷,也等價於 ${name} = null 表示式。

3.2.1.7 不等於空

  • 用於判斷值是否不為null,使用 is not null 關鍵字,關鍵字不區分大小寫, is、IS、not、Not、null、Null、NULL等都能識別。
  • 格式為 keyName is not null, 例如 ${name} is not null 表示name屬性值不為null的判斷,也等價於 ${name} != null 表示式。

3.2.2 其他說明:

  • where條件支援使用比較符合,具體符合和比較規則,詳見 4.1 比較說明
  • 當使用單指作為條件時,必須是布林型別,或者是可以轉換為布林型別的數字或字串,否則語法執行將報錯。布林型別轉換詳見4.2 布林型別的轉換

3.3 Select對映語法

3.3.1 使用格式:

  • 直接寫需要對映的欄位,欄位間使用英文逗號分隔,例如: name1, name2, name3, name4 ...

3.3.1 指定對映別名:

  • 預設不指定的情況下,則直接用原欄位名稱,例如: ${name1}, ${name2} 表示取原物件的name1和name2屬性,並對映賦值到目標物件的name1和name2屬性。


    如果是使用${}動態取值,預設情況下對映到目標物件的名稱為取值欄位名,例如${name} 表示取原物件的name屬性,並對映賦值到目標物件的name屬性。


    如果是F{}函式,預設情況下對映到目標物件的名稱為函式名,例如F{ClassSimpleName(${name})} 表示取原物件的name屬性,並執行ClassSimpleName函式後,將結果對映賦值到目標物件的ClassSimpleName屬性。


  • 使用空格方式指定。例如:${name1} fatherName, ${name2} motherName 表示取原物件的name1和name2屬性,並對映賦值到目標物件的fatherName和motherName屬性。


  • 使用AS關鍵字指定,並且as關鍵字不區分大小寫。例如:${name1} AS fatherName, ${name2} as motherName 表示取原物件的name1和name2屬性,並對映賦值到目標物件的fatherName和motherName屬性。


四、約定說明

4.1 比較說明

  • 目前支援的比較符號如下

    符號 含義
    > 大於
    < 小於
    = 等於
    >= 大於等於
    <= 小於等於
    != 不等於
    <> 不等於
  • 支援不同型別進行比較,規則如下:

    1. 判斷相等時:如果是相同型別,判斷相等時直接呼叫物件的equalse方法比較。
    2. 判斷相等時:如果比較物件有null,則要求另外個物件也必須為null才相等,否則均為不等。
    3. 判斷相等時:如果比較物件有個為布林型別,另外個物件為字串或數字時,則嘗試統一轉換為布林值進行比較。0和false字串都屬於假,1和true字串則屬於真(false和true字串不區分大小寫)。
    4. 判斷相等時:如果都是同屬於Number子類,則轉換為浮點型比較。
    5. 判斷相等時:如果都是屬於其中一種(java原始型別、字串型別、Number子類),統一獲取物件字串進行比較。
    6. 判斷大小時:如果存在有物件為null,屬於無法比較大小,均返回false。
    7. 判斷大小時:如果物件均為BigDecimal,轉為BigDecimal進行比較
    8. 判斷大小時:如果都是同屬於Number子類,則轉換為浮點型比較。
    9. 判斷大小時:如果存在有物件為布林值,屬於無法比較大小,均返回false。
    10. 判斷大小時:如果都是屬於其中一種(java原始型別、字串型別、Number子類),統一獲取物件字串進行比較(先比較長度,然後在比較asscii碼值)。

4.2 布林型別的轉換

  • 支援原生的布林型別
  • 如果是數字型別(Number子類),0屬於假可以轉換為false布林值,1屬於真可以轉換為true布林值。
  • 如果是字串型別,"false"、'false'字串屬於假可以轉換為false布林值,"true"、'true'字串屬於真可以轉換為true布林值,並且字串不區分大小寫。

4.3 關鍵字大小寫

除特殊情況外,預設情況下,關鍵字是不區分大小寫的,均能識別,可根據需要自行選擇使用。例如 And、AND、and、As、AS、as 等關鍵字都能識別。

4.4 執行緒安全

凡是對應的介面或者類名上有com.cat.oqj4j.annotation.ThreadSafe註解的,均表示此介面實現類和指定類都是執行緒安全的,生成的物件可作為單例使用。

五、高階應用

5.1 自定義函式

  • 除了使用框架預定義的函式以外,還支援自定義函式,方式如下:
    1. 實現com.cat.oqj4j.antlr.handler.FunHandler介面,或者繼承com.cat.oqj4j.antlr.handler.AbstractFunHandler類。注意實現類必須是執行緒安全的。
    2. 註冊函式,在OqlClientBuilder構造時註冊,然後在表示式中透過F{}即可使用。
    OqlClientBuilder oqlClientBuilder = new OqlClientBuilder();
    // MyFunHandler是你自定義的函式類,根據需要更改類名。注意MyFunHandler是滿足執行緒安全的。
    oqlClientBuilder.registerFunHandler(new MyFunHandler());

5.2 自定義bean操作

  • 對bean的設值和賦值的操作,目前框架使用的是ApacheUtilsBeanHandler,此類是基於apache的commons-beanutils工具包的PropertyUtils類來實現,如果需要者可以自定義bean操作類,方式如下:
    1. 實現com.cat.oqj4j.support.BeanHandler介面。注意實現類必須是執行緒安全的。
    2. 設定bean操作器,在OqlClientBuilder構造時設定,後續對bean的操作都會自動用此操作器。
    OqlClientBuilder oqlClientBuilder = new OqlClientBuilder();
    // MyBeanHandler是你實現的bean操作器,根據需要更改類名。注意MyBeanHandler是滿足執行緒安全的。
    oqlClientBuilder.setBeanHandler(new MyBeanHandler());

附加

原始碼將釋出在github上進行管理,同時在gittee進行放開檢視。

  • github地址(支援編輯): https://github.com/cat4416/oql4j
  • gitee地址(僅能檢視):https://gitee.com/cat4416/oql4j

作者聯絡方式:

  • 微訊號(wechat):cat4416
  • 郵箱:565335545@qq.com

相關文章