一個朋友的架構釋出1.1版本,和1.0在運作時生成額外位元組碼不同,1.1在編譯時通過maven插件生成額外位元組碼,是以避免了暴露微量的設計模式給使用者,讓用法更簡單粗暴,性能也更高。很大一部分不太像架構了,而更像是java語言的擴充,是個重大更新。此外,充分吸取1.0的教訓,給出架構圖并,讓所有文檔中英文雙版,避免别人不明全局。另外,不再相容java7。
babyfish是一款讓開發人員僅通過聲明式代碼的方式實作智能資料結構的java架構; 其功能非常豐富,請閱讀下面這張圖檔以對其大體架構有個粗略了解。
在這張圖中,有5個功能點被紅色星号标注:
<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#unstablecollectionelements">unstable collection elements</a>
<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#bubbleevent">bubble event</a>
<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#objectmodel4java">objectmodel4java</a>
<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#objectmodel4jpa">objectmodel4jpa</a>
<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#querypath">query path</a>
,它們是本架構最重要的5個功能點。
babyfish1.1需要java8
文檔
描述
圖文并茂地詳細講述部分重要功能(請使用高版本ie或其它浏覽器打開)。
逐個介紹每個demo,并給出建議的閱讀順序(請使用高版本ie或其它浏覽器打開)。
提供一種更好的實作國際化的方法。和傳統的使用在運作時刻報錯的java.util.resourcebundle進行開發不同, 請類型國際化的所有錯誤均在編譯時刻報告給開發人員(此功能需要編譯時位元組碼增強)。
更好的事件通知模型, 如同.net的委托一樣,相比于java bean規範的建議更簡單、更節約、更強大 (此功能需要編譯時位元組碼增強)。
開發人員往往需要覆寫“public boolean equals(object o)”方法, 通常他們有兩種檢查參數對象的類型是否和目前對象(this)的類型相同的辦法。
過于嚴格的辦法: if (this.getclass() != o.getclass()) return false;
過于寬松的辦法:if (!(o instanceof ${thisclass})) return false;
不幸的是,兩種都是錯的,本功能提供一種實作“equals”方法的完美方法, 其相應的demo指出它們各自的問題并給出正确的解決方案。
此功能用于深度優先或廣度優先的對象圖周遊,周遊過程中提供豐富的周遊上下文變量。
“x”代表“extensible”, 為了實作一些不可思議的功能,x集合架構擴充了java集合架構的接口并給出一套全新的實作。
和“org.apache.commons.collections4.bidimap”類似, 為了讓“java.util.map” 支援值(非鍵)的唯一性; x集合架構支援bidmap;同理,也支援bidilist。
java集合架構支援哈希結構和紅黑樹結構,例如hashmap, treeset and treemap; 它們的好處是高性能,但是有一個缺陷,當某個對象被作為元素添加到set或被作為鍵添加到map,它的資料就不能被修改了。
x集合架構支援一個“不穩定集合元素的功能“,即便某個對象被作為元素添加到set或被作為鍵添加到map, 它也可以被修改,因為與之相關的所有set和map集合均會在其被修改時自動修改。
集合類型
不穩定集合元素能力展現
set
不穩定元素
map
不穩定鍵
bidilist
bidimap
不穩定值
“ma”代表“modification aware”,ma集合架構擴充至x集合架構,使用者支援資料變更事件通知。 對于每一個被修改的元素或鍵值對,兩個事件将會被觸發,其中一個在修改之前觸發,而另外一個在修改後觸發。 這個功能和關系型資料庫的行級觸發器極為類似。
在babyfish集合架構中,集合對象的資料不僅會被顯式的api調用修改,有時也會被系統自動修改。其中,最典型的代表就是“不穩定集合元素”導緻的集合被自動修改的案例了。 這種不是由開發人員發起的、自動的、隐式的集合修改照樣會導緻事件的觸發。
java集合架構是支援集合視圖的,例如
“java.util.navigablemap”支援headmap、tailmap、submap和descendingmap方法。
“java.util.navigableset”支援headset、tailset、subset、descendingset和descendingiterator方法。
“java.util.list”支援sublist和listiterator方法。
“java.util.map”支援keyset、values和entryset方法。
“java.util.collection”支援iterator方法。
這表示開發人員可以基于原始集合或視圖集合建立新的視圖集合,所有視圖集合均和原始集合共享同一份資料,其中任何一個被修改時,原始集合均會受到影響。
ma集合架構支援一個叫“冒泡事件“的功能,當視圖集合被修改的時候,事件将會在此視圖對象上被觸發;然後,事件向上冒泡,讓上一層視圖集合觸發事件;以此類推,最終,冒泡到根部的原始集合,事件将在原始集合上被觸發。
我們知道,orm架構常常支援一種叫延遲加載的技術,延遲集合被允許是虛假的,并不包含任何資料,直到第一次被開發人員使用時,才會通過io操作從外界擷取資料進而轉變成真實集合。 這是一個很強大的功能,如此好的功能如果隻能在orm領域使用,就是太浪費了。
為了讓這種強大的功能可以在任何場合被使用(而非僅僅在orm實作中實作),babyfish支援抽象而普适的延遲集合架構。
沒有相應的demo,因為這是架構的spi,并非api。
java集合架構附帶了一個強大的工具類java.util.collections, 此類提供很多靜态方法以建立神奇的集合代理對象。
babyfish集合架構擴充了java集合架構的接口,類似的,它需要提供自己的工具類:org.babyfish.collection.macollections"。
objectmodel為babyfish架構的核心功能,也是我開發此架構的原因。
objectmodel用于建構智能資料結構,又可分為兩個部分
<a></a>
objectmodel4java:在java語言層面支援智能資料結構,這是本章節将重點介紹的功能。
java開發人員往往将資料模型類聲明成除了getter和setter通路器外沒有任何邏輯的簡單類 (說直白點,就是c語言結構體),這種簡單的類所描述的資料結構的智能性和友善性非常有限。 舉一簡單的例子,對于兩個對象之間的雙向關聯,開發人員其中一端時,另外一端沒有對應地修改, 就會導緻資料不一緻,通過某個對象的關系能導航到另外一個對象,但反過來不成立。
objectmodel4java在為資料結構引入智能性的同時,并沒有增加開發的複雜程度。畢竟,除了getter和setter通路器 之外無任何邏輯的資料類寫起來是非常簡單的,這種簡單的代碼書寫方式早已深入人心。objectmodel4java 隻需要開發人員在這些簡單的資料類上添加一點注解,借助于編譯時的位元組碼增強maven插件,開發人員便可以得到功能強大的資料模型。
下面我們來看一個實際的例子,建立部門和員工之間的雙向一對多關系。
一個部門可以包含多個員工,且這些員工是有先後順序的。
假設現有6個對象,其變量名分别為department1, department2, employee1, employe2, employee3和employee4,它們的資料如下:
部門對象
員工對象
變量名
employees
department
index
department1
[ employee1, employee2 ]
employee1
employee2
1
department2
[ employee3, employee4 ]
employee3
employee4
此處,僅舉一例,使用者使用如下代碼修改department1對象的employees集合
此語句視圖将employee3插入到department1.employees集合的最前面, 為了維持資料結構一緻性,objectmodel4java将會執行如下調整。
自動将employee3從department2.employees集合中删除。
自動将employee3.department設定為department1。
自動将employee1的index從0變成1。
自動将employee2的index從1變成2。
自動将employee4的index從1變成0。
最終,資料結構變成這樣
[ employee3, employee1, employee2 ]
2
[ employee4 ]
在上個例子中,關聯字段使用了java.util.list集合,除此之外,集合關聯屬性還可以使用java.util.set或java.util.map。 接下來的例子中,我們使用java.util.set來描述公司和投資人之間的雙向多對多關系。
一個公司可以由多個投資人投資。
假設現有3個對象,其中的一個company類型的對象,變量名為apple,還有兩個investor對象,變量名為steve和sculley。 它們的資料如下:
公司對象
投資人對象
shortname
investors
name
companies
apple
[ steve, sculley ]
steve
[ apple ]
sculley
現在開發人員執行如下一句代碼:
[ sculley ]
[]
為了讓實體對象的集合字段能具備以上所有能力,babyfish擴充lazy集合架構,給出了 自己的hibernate集合實作。
為了在查詢業務支援支援動态的貪懶加載(關聯對象的抓取方式由業務層和表示層的通過傳遞參數動态決定,而非在資料層内部寫死實作), babyfish支援一個叫“query path”的功能,它看起來非常類似于
<a href="http://api.rubyonrails.org/classes/activerecord/querymethods.html#method-i-includes">active record的貪婪加載能力</a>
<a href="https://msdn.microsoft.com/en-us/library/bb738708(v=vs.110).aspx">ado.net entity framework的貪婪加載能力</a>
active record和ado.net entity framework的包含路徑是類型不安全的字元串, 錯誤路徑的錯誤要等到運作或測試程式才會被報告。 而query path則通過maven插件在編譯時生成query path元模型源碼的方式讓路徑強類型化,錯誤的路徑會在編譯時報錯。
querypath不僅僅可以以目前被查詢對象為核心對任意深度和廣度的的關聯屬性進行貪婪加載,還可以對延遲的标量屬性(不一定但往往是大字段)進行貪婪加載。
querypath還可以對被查詢對象本身以及對象之間的集合關聯進行排序。
hibernate有一個性能缺陷。當某個查詢既包含對集合關聯的抓取(join fetch)又具備分頁條件時, hibernate無法通過sql語句實作分頁查詢,而是先不分頁地查詢出所有滿足條件的對象,然後在記憶體中完成分頁篩選, 同時在日志檔案中列印如下警告:
"firstresult/maxresults specified with collection fetch; applying in memory!"
針對oracle資料庫,babyfish能解決此問題,隻需要開發使用babyfish擴充過的oracle方言即可,如下:
<a href="https://github.com/babyfish-ct/babyfish/blob/master/src/babyfish-hibernate-extension/src/main/java/org/babyfish/hibernate/dialect/oracle8idialect.java">org.babyfish.hibernate.dialect.oracle8idialect</a>
<a href="https://github.com/babyfish-ct/babyfish/blob/master/src/babyfish-hibernate-extension/src/main/java/org/babyfish/hibernate/dialect/oracle9idialect.java">org.babyfish.hibernate.dialect.oracle9idialect</a>
<a href="https://github.com/babyfish-ct/babyfish/blob/master/src/babyfish-hibernate-extension/src/main/java/org/babyfish/hibernate/dialect/oracle10gdialect.java">org.babyfish.hibernate.dialect.oracle10gdialect</a>
2008年8月: 我有了某些點子呢, 開始利用工作外的業餘時間開發此架構。 2015年10月: 完成第一個版本1.0.0并上傳至github 2016年6月: 完成1.1.0版本。
2016年6月25日于成都。