jOOQ API设计缺陷的奇怪发生
jOOQ是一種內部領域特定語言(DSL) ,它以Java(宿主語言)建模SQL語言(外部DSL)。 這篇熱門文章描述了jOOQ API的主要機制:
Java Fluent API設計器速成課程 。
任何人都可以根據該文章中的規則以Java(或大多數其他主機語言)實現內部DSL。
SQL語言功能示例:BOOLEAN
但是,關于SQL語言的優點之一是BOOLEAN類型,該類型在SQL:1999以后才引入到該語言中。 當然,沒有布爾值,您可以通過1和0建模TRUE和FALSE值,并使用CASE將謂詞轉換為值
CASE WHEN A = B THEN 1 ELSE 0 END但是有了真正的BOOLEAN支持,您可以進行出色的查詢,例如針對Sakila數據庫運行的以下PostgreSQL查詢:
SELECTf.title, string_agg(a.first_name, ', ') AS actors FROM film AS f JOIN film_actor AS fa USING (film_id) JOIN actor AS a USING (actor_id) GROUP BY film_id HAVING every(a.first_name LIKE '%A%')以上收益:
TITLE ACTORS ----------------------------------------------------- AMISTAD MIDSUMMER CARY, DARYL, SCARLETT, SALMA ANNIE IDENTITY CATE, ADAM, GRETA ANTHEM LUKE MILLA, OPRAH ARSENIC INDEPENDENCE RITA, CUBA, OPRAH BIRD INDEPENDENCE FAY, JAYNE ...換句話說,我們正在尋找所有電影,其中在電影中扮演過的所有演員的名字中都包含字母“ A”。 這是通過布爾表達式/謂詞first_name LIKE '%A%'上的聚合來完成first_name LIKE '%A%' :
HAVING every(a.first_name LIKE '%A%')現在,按照jOOQ API的術語,這意味著我們將不得不提供having()不同參數類型的hading having()方法的重載,例如:
// These accept "classic" predicates having(Condition... conditions); having(Collection<? extends Condition> conditions);// These accept a BOOLEAN type having(Field<Boolean> condition);當然,這些重載可用于任何接受謂詞/布爾值的API方法,而不僅僅是HAVING子句。
如前所述,從SQL:1999開始,jOOQ的Condition和Field<Boolean>確實是同一回事。 jOOQ允許通過顯式API在兩者之間進行轉換:
Condition condition1 = FIRST_NAME.like("%A%"); Field<Boolean> field = field(condition1); Condition condition2 = condition(field);…和重載使轉換更方便地隱式進行。
所以有什么問題?
問題在于,我們認為添加另一個方便的重載,即having(Boolean)方法可能是個好主意,在having(Boolean)方法中,為了方便起見,可以在查詢中引入常量,可為空的BOOLEAN值,這在構建動態SQL時很有用。 ,或注釋掉某些謂詞:
DSL.using(configuration).select().from(TABLE).where(true) // .and(predicate1).and(predicate2) // .and(predicate3).fetch();這個想法是,無論您要臨時刪除哪個謂詞,都不會將WHERE關鍵字注釋掉。
不幸的是,添加此重載給使用IDE自動完成功能的開發人員帶來了麻煩。 考慮以下兩個方法調用:
// Using jOOQ API Condition condition1 = FIRST_NAME.eq ("ADAM"); Condition condition2 = FIRST_NAME.equal("ADAM");// Using Object.equals (accident) boolean = FIRST_NAME.equals("ADAM");通過(偶然地)在equal()方法中添加字母“ s” equal()主要是由于IDE自動補全),整個謂詞表達式從可用于生成SQL的jOOQ表達式樹元素到“普通”布爾值,極大地改變了語義。值(顯然總是產生false )。
在添加最后一個重載之前,這不是問題。 equals()方法的用法無法編譯,因為沒有適用的重載采用Java boolean類型。
// These accept "classic" predicates having(Condition condition); having(Condition... conditions); having(Collection<? extends Condition> conditions);// These accept a BOOLEAN type having(Field<Boolean> condition);// This method didn't exist prior to jOOQ 3.7 // having(Boolean condition);在jOOQ 3.7之后,由于編譯器不再抱怨,從而導致錯誤的SQL,這種事故開始在用戶代碼中引起注意。
您繼承了宿主語言的“缺陷”
Java是“有缺陷的”,因為保證每種類型都可以繼承自java.lang.Object及其方法: getClass() , clone() , finalize() equals() , hashCode() , toString() , notify() , notifyAll()和wait() 。
在大多數API中,這實際上并不是什么大問題。 您實際上并不需要重用上述任何方法名(請不要) 。
但是在設計內部DSL時,這些Object方法名稱(就像language關鍵字一樣)限制了您的設計空間。 在equal(s)的情況下尤其明顯。
我們已經學習,并且已經棄用,并且將移除having(Boolean)重載 ,并再次移除所有類似的重載。
翻譯自: https://www.javacodegeeks.com/2016/01/curious-incidence-jooq-api-design-flaw.html
總結
以上是生活随笔為你收集整理的jOOQ API设计缺陷的奇怪发生的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蔡崇信、吴泳铭正式履新阿里巴巴董事长、C
- 下一篇: B 社陶德:《星空》会推出 DLC,但何