關於或邏輯的思考 java
本篇文章咱們來探討如何使用 flying 的方式來描述帶有 」or」 關鍵字的 sql 語句(若是您對 flying 還不瞭解,請參見 https://www.oschina.net/p/flying)。一直以來,flying力求作到的就是,把每一次與數據庫交互都變爲對象交互,而不是字符串交互,由於對象相比字符串至少有如下好處:git
不一樣數據庫的 sql 語法有區別,但它們的查詢對象相同。sql
對象能夠跨語言,能夠以 json 方式傳輸保存。數據庫
爲了作到以上這些點,做爲條件的查詢對象必須具備如下特色:json
第一個特性很好理解,例如 personCondition.setNameLike(「張」); 和 personCondition.setAge(30); 就是順序無關的,flying查詢對象目前全部的條件賦值語句(包括判斷條件、分頁條件、排序條件)都是順序無關的。 數組
第二個特性是,用戶一眼看到某個變量賦值語句就知道它的做用是什麼,並能夠按須要進行修改。mybatis
第三個特性是,全部的條件變量其實都是用與邏輯「and」相連的。併發
看到這裏,你們會發現,其實 flying 查詢對象只解決了一半的問題,由於對於或操做 「or」,之前根本就沒有說起,而沒說起的緣由是,在知足以上三點的基礎上解決或邏輯比較困難。而本文則嘗試解決這一問題。app
首先,咱們拋出一個足夠複雜的sql語句:工具
select person.id, person.name, person.age, person.level from person where (person.name like ‘張%’ and person.age = 25) or (person.age = 27 and person.level = ‘B’) or (person.name like ‘李%’ and person.level = ‘A’)
這個複雜的sql語句如何用一個查詢對象表示呢?這裏咱們須要使用一些數學工具,首先咱們用邏輯變量來代替條件表達式:
A = "person.name like '張%'"
B = "person.age = '25'"
C = "person.age = '27'"
D = "person.level = 'B'"
E = "person.name like '李%'"
F = "person.level = 'A'"
這樣一來以上這個邏輯表達式就簡化爲:(A∩B)∪( C∩D)∪( E∩F)
但是這樣沒法解決問題,由於 flying 擅長解決的是以「且」關係鏈接的條件,例如 X∩Y∩Z 這樣,而以上表達式明顯不是這樣。
可是布爾邏輯運算具備如下性質:交換律、結合律與分配律。
交換律:A∩B = B∩A
同理 A∪B = B∪A
結合律:A∩(B∩C) = A∩B∩C
同理 A∪(B∪C)= A∪B∪C
分配律:(A∩B)∪C = (A∪C)∩(B∪C)
同理(A∪B)∩C = (A∩C)∪(B∩C)
有了這三個定律以後,咱們就能夠把(A∩B)∪( C∩D)∪( E∩F)變形爲一連串布爾變量以「∩」相連的形式:
(A∩B)∪(C∩D)∪( E∩F)
= (((A∩B)∪C)∩((A∩B)∪D)))∪( E∩F)
= (((A∪C)∩(B∪C))∩((A∪D)∩(B∪D)))∪( E∩F)
= ((A∪C)∩(B∪C)∩(A∪D)∩(B∪D))∪(E∩F)
= (((A∪C)∩(B∪C)∩(A∪D)∩(B∪D))∪E)∩(((A∪C)∩(B∪C)∩(A∪D)∩(B∪D))∪F)
= (A∪C∪E)∩(B∪C∪E)∩(A∪D∪E)∩(B∪D∪E)∩(A∪C∪F)∩(B∪C∪F)∩(A∪D∪F)∩(B∪D∪F)
最後的這個形式看起來是咱們用 flying 能描述的了的。實際上,對於布爾運算式有如下定理:
任何一個布爾表達式都能被轉換爲一個等價的合取範式(CNF),合取範式格式爲:C1∩C2∩……Cn;其中,Ck(1<=k<=n)稱爲合取項,每一個合取項是不包含∩的表達式。
這個歸併是關係型數據庫本身也會作的,由於它具備如下好處:
若是一個合取項上存在索引,則先判斷索引是否可用,如能利用索引快速得出合取項的值,則能加快判斷速度。如:WHERE (A.a> 100 AND A.b = 5 AND... )
狀況1:A表的a列上存在索引,b列無索引,則利用a上的索引找出元組,「A.b = 5」 做爲過濾條件使用;狀況2:A表的a列上不存在索引,b列有索引,則利用b上的索引找出元組,「A.a> 100」 做爲過濾條件使用。因此,相對於(A∩B)∪( C∩D)∪( E∩F),咱們將(A∪C∪E)∩(B∪C∪E)∩(A∪D∪E)∩(B∪D∪E)∩(A∪C∪F)∩(B∪C∪F)∩(A∪D∪F)∩(B∪D∪F)傳給數據庫,並不會增長它的查詢時間,由於它本來也須要歸併。
那麼接下來的問題就變成,咱們如何用代碼描述(A∪C∪E),或者更具體地說,如何用代碼描述:"person.name like '張%' or person.age = 27 or person.name like '李%'" 這樣一個查詢條件,這個解決了其它查詢條件同理也就都解決了。
在這裏,flying新增了Or標籤類(見https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/annotations/Or.java),這個標籤的內容是ConditionMapperAnnotation標籤的數組,因此在查詢條件類中能夠有以下標籤代碼:
@Or({ @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike), @ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.Equal), @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike) })
同時爲了賦值方便,咱們強烈建議採用不定參數的Object[]做爲變量,因而整個代碼變成了:
@Or({ @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike), @ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.Equal), @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike) }) private Object[] condition1; public Object[] getCondition1 () { return condition1; } public void setCondition1 (Object... condition1) { this. condition1 = condition1; }
咱們描述 "person.name like '張%' or person.age = 27 or person.name like '李%' "的代碼變爲:
personCondition.setCondition1("張", 27, "李"); /* 注意參數順序和 condition1 上 @ConditionMapperOrAnnotation 的內部順序一致 */
因而問題就全解決了。您也許會以爲這個解決方案過於複雜,但對於(A∩B)∪( C∩D)∪( E∩F)來講,用其它代碼方式描述同樣複雜(純sql除外,但咱們知道使用查詢對象代替 sql 的好處)。
接下來咱們再給出一些常見一點的使用或邏輯的場景,例如我想選擇全部30歲如下或50歲以上的人員:
@Or({ @ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.LessThan), @ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.GreaterThan) }) private Object[] ageFilter; public Object[] getAgeFilter () { return ageFilter; } public void setAgeFilter (Object... ageFilter) { this. ageFilter = ageFilter; } personCondition.setAgeFilter(30,50);
或者咱們找全部姓張或者姓李的人:
@Or({ @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike), @ConditionMapperAnnotation(dbFieldName = "name", conditionType = ConditionType.HeadLike)}) private Object[] nameFilter; /* 相關getter和setter請自行添加 */ personCondition.setAgeFilter("張", "李");
或者咱們找年齡在 40 以上或者 level 爲 A 的人:
@Or({ @ConditionMapperAnnotation(dbFieldName = "age", conditionType = ConditionType.GreaterThan), @ConditionMapperAnnotation(dbFieldName = "level", conditionType = ConditionType.Equals)}) private Object[] filter; /* 相關getter和setter請自行添加 */ personCondition.setFilter(40, "A");
是否是使用起來仍是挺簡單的?flying的設計哲學是「使您寫下的每一行代碼的回報率最大化」,當您的項目變得愈來愈龐大時,您會愈來愈明顯感覺到這一點。
自定義主鍵生成器
flying-初雪另外一個特點是增長了自定義主鍵生成器,爲此咱們在flying:insert語句中新增了括號元素,好比:
flying:insert(uuid) 使用標準uuid做主鍵
flying:insert(uuid_no_line) 使用無下橫線的uuid做主鍵
flying:insert(millisecond) 使用毫秒數做主鍵(需保證每秒併發在1000如下)
以上這些能夠在 https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/statics/KeyGeneratorType.java 看到,固然更多的狀況是您會自定義本身的主鍵生成器,只要您的主鍵生成器實現了 flying 中的 indi.mybatis.flying.type.KeyHandler 接口便可,好比這樣調用一個自定義的主鍵生成器類:
flying:insert(indi.mybatis.flying.handlers.MySnowFlakeKeyHandler)
(上面的 indi.mybatis.flying.handlers.MySnowFlakeKeyHandler 是一個雪花主鍵生成器的 java 版本實現。雪花主鍵生成器由 tweeter 發明用於處理大規模並行寫入,主鍵採用 float 類型存儲以節省資源,自帶遞增無需 order by,單臺主機每秒可產生 400 萬個不一樣主鍵,最多可 1024 臺主機集羣同時工做)
或者您有某幾個表的主鍵要共享一個連續數列的需求(好比工做流),就能夠開發本身的主鍵生成器。
總結