javascript
JSR-308和Checker框架为jOOQ 3.9添加了更多类型安全性
Java 8引入了JSR-308,它為Java語言添加了新的注釋功能。 最重要的是:鍵入注釋。 現在可以像下面這樣設計怪物了:
比注解更瘋狂的是類型注解。 在數組上。 誰認為這是有效的Java代碼? pic.twitter.com/M9fSRRerAD
— Lukas Eder(@lukaseder) 2016年3月20日
該推文中顯示的代碼確實可以編譯。 現在可以注釋每種類型,以便以任何自定義方式增強類型系統。 為什么,你可能會問? 這種語言增強的主要驅動用例之一是checker框架 ,這是一個開放源代碼庫,可讓您輕松實現任意編譯器插件以進行復雜的類型檢查。 最無聊和瑣碎的例子是可空性。 考慮以下代碼:
import org.checkerframework.checker.nullness.qual.Nullable;class YourClassNameHere {void foo(Object nn, @Nullable Object nbl) {nn.toString(); // OKnbl.toString(); // Failif (nbl != null)nbl.toString(); // OK again} }上面的示例可以直接在checker框架實時演示控制臺中運行 。 使用以下注釋處理器編譯以上代碼:
javac -processor org.checkerframework.checker.nullness.NullnessChecker afile.java
產量:
錯誤:[dereference.of.nullable]取消引用可能為空的引用nbl:5:9
太棒了! 例如,它的工作方式與在Ceylon或Kotlin中 實現的流敏感類型非常相似,不同之處在于它更為冗長。 但是它也要強大得多,因為可以使用注釋處理器直接在Java中實現實現增強的和帶注釋的Java類型系統的規則! 通過某種方式使注解圖靈完整。��
這對jOOQ有什么幫助?
jOOQ已經提供了兩種類型的API文檔注釋。 這些注釋是:
- @PlainSQL –表示DSL方法接受“純SQL”字符串,這可能會帶來SQL注入風險
- @Support –表示DSL方法可以本機工作,或者可以針對給定的SQLDialect集進行仿真
這種方法的一個示例是CONNECT BY子句 ,該子句得到Cubrid,Informix和Oracle的支持,為了方便起見,它也被重載為也接受“普通SQL”謂詞:
@Support({ CUBRID, INFORMIX, ORACLE }) @PlainSQL SelectConnectByConditionStep<R> connectBy(String sql);到目前為止,這些注釋僅用于文檔目的。 使用jOOQ 3.9后,不再可用。 現在,我們向jOOQ API引入了兩個新的注釋:
- org.jooq.Allow –允許在給定范圍內使用一組方言(或@PlainSQL批注)
- org.jooq.Require –在給定范圍內要求通過@Support注釋支持一組方言
最好通過示例來解釋。 讓我們先看看@PlainSQL
限制對
使用jOOQ API的最大優點之一就是SQL注入已經成為過去。 由于jOOQ是內部特定于域的語言,因此用戶確實可以直接在Java代碼中直接定義SQL表達式樹,而不是像JDBC那樣使用聲明的字符串化版本。 表達式樹是用Java編譯的,因此不可能通過用戶輸入注入任何不需要的或無法預見的表達式。
但是有一個例外。 jOOQ并不支持每個數據庫中的所有SQL功能。 這就是jOOQ附帶豐富的“普通SQL” API的原因,在該API中,可以將自定義SQL字符串嵌入SQL表達式樹中的任何位置。 例如,上面的CONNECT BY子句:
DSL.using(configuration).select(level()).connectBy("level < ?", bindValue).fetch();上面的jOOQ查詢轉換為以下SQL查詢:
SELECT level FROM dual CONNECT BY level < ?如您所見,完全有可能“做錯”并產生SQL注入風險,就像在JDBC中一樣:
DSL.using(configuration).select(level()).connectBy("level < " + bindValue).fetch();區別非常細微。 使用jOOQ 3.9和checker框架,現在可以指定以下Maven編譯器配置:
<plugin><artifactId>maven-compiler-plugin</artifactId><version>3.3</version><configuration><source>1.8</source><target>1.8</target><fork>true</fork><annotationProcessors><annotationProcessor>org.jooq.checker.PlainSQLChecker</annotationProcessor></annotationProcessors><compilerArgs><arg>-Xbootclasspath/p:1.8</arg></compilerArgs></configuration> </plugin>org.jooq.checker.PlainSQLChecker將確保不會編譯使用帶有@PlainSQL注釋的API的客戶端代碼。 我們收到的錯誤消息是這樣的:
C:\ Users \ lukas \ workspace \ jOOQ \ jOOQ-examples \ jOOQ-checker-framework-example \ src \ main \ java \ org \ jooq \ example \ checker \ PlainSQLCheckerTests.java:[17,17]錯誤:[普通]當前范圍不允許使用SQL。 使用@ Allow.PlainSQL。]
如果您知道自己在做什么,并且絕對必須在非常特定的位置(范圍)使用jOOQ的@PlainSQL API,則可以使用@Allow.PlainSQL對該位置(范圍)進行注釋,并且代碼可以再次正常編譯:
// Scope: Single method. @Allow.PlainSQL public List<Integer> iKnowWhatImDoing() {return DSL.using(configuration).select(level()).connectBy("level < ?", bindValue).fetch(0, int.class); }甚至:
// Scope: Entire class. @Allow.PlainSQL public class IKnowWhatImDoing {public List<Integer> iKnowWhatImDoing() {return DSL.using(configuration).select(level()).connectBy("level < ?", bindValue).fetch(0, int.class);} }甚至(但是您可能只是關閉檢查器):
// Scope: entire package (put in package-info.java) @Allow.PlainSQL package org.jooq.example.checker;好處是顯而易見的。 如果安全性對您非常重要(應該如此),則只需在每個開發人員版本或至少在CI版本中啟用org.jooq.checker.PlainSQLChecker ,并在“偶然”使用@PlainSQL API時獲得編譯錯誤遇到。
限制對
現在,對于大多數用戶而言,更有趣的是能夠檢查客戶端代碼中使用的jOOQ API是否確實支持您的數據庫。 例如,上面的CONNECT BY子句僅在Oracle中受支持(如果我們忽略不太流行的Cubrid和Informix數據庫)。 假設您僅使用Oracle。 您要確保您使用的所有jOOQ API都與Oracle兼容。 現在,您可以將以下注釋添加到所有使用jOOQ API的軟件包中:
// Scope: entire package (put in package-info.java) @Allow(ORACLE) package org.jooq.example.checker;現在,只需激活org.jooq.checker.SQLDialectChecker來鍵入代碼以檢查@Allow符合性,即可完成:
<plugin><artifactId>maven-compiler-plugin</artifactId><version>3.3</version><configuration><source>1.8</source><target>1.8</target><fork>true</fork><annotationProcessors><annotationProcessor>org.jooq.checker.SQLDialectChecker</annotationProcessor></annotationProcessors><compilerArgs><arg>-Xbootclasspath/p:1.8</arg></compilerArgs></configuration> </plugin>從現在開始,每當您使用任何jOOQ API時,上述檢查器都將驗證以下三個值是否為true:
- 正在使用的jOOQ API未使用@Support注釋
- 使用的jOOQ API帶有@Support注釋,但沒有任何顯式的SQLDialect (即“可在所有數據庫上工作”),例如DSLContext.select()
- 使用的jOOQ API帶有@Support注釋,并帶有SQLDialects引用的至少一個@Allow
因此,在這樣標注的包裝中……
// Scope: entire package (put in package-info.java) @Allow(ORACLE) package org.jooq.example.checker;……使用這樣注釋的方法就可以了:
@Support({ CUBRID, INFORMIX, ORACLE }) @PlainSQL SelectConnectByConditionStep<R> connectBy(String sql);…但是使用這樣注釋的方法不是:
@Support({ MARIADB, MYSQL, POSTGRES }) SelectOptionStep<R> forShare();為了允許使用此方法,例如,客戶端代碼除了可以使用ORACLE語言外,還可以使用MYSQL語言:
// Scope: entire package (put in package-info.java) @Allow({ MYSQL, ORACLE }) package org.jooq.example.checker;從現在開始,此程序包中的所有代碼都可能引用支持MySQL和/或Oracle的方法。
@Allow批注有助于在全局級別上訪問API。 多個@Allow注釋(范圍可能不同)創建了允許的方言的@Allow取關系,如下所示:
// Scope: class @Allow(MYSQL) class MySQLAllowed {@Allow(ORACLE)void mySQLAndOracleAllowed() {DSL.using(configuration).select()// Works, because Oracle is allowed.connectBy("...")// Works, because MySQL is allowed.forShare();} }從上面可以看出,析取兩個方言不能確保給定的語句在兩個數據庫中都可以使用。 所以…
如果我希望同時支持兩個數據庫怎么辦?
在這種情況下,我們將使用新的@Require注釋。 多個@Require注釋(范圍可能不同)創建所需方言的合集,如下所示:
// Scope: class @Allow @Require({ MYSQL, ORACLE }) class MySQLAndOracleRequired {@Require(ORACLE)void onlyOracleRequired() {DSL.using(configuration).select()// Works, because only Oracle is required.connectBy("...")// Doesn't work because Oracle is required.forShare();} }如何使用
假設您的應用程序僅需要與Oracle一起使用。 現在,您可以在軟件包上添加以下注釋,例如,由于在您的代碼中不允許將MySQL作為方言,因此您將無法使用任何僅MySQL的API:
@Allow(ORACLE) package org.jooq.example.checker;現在,隨著需求的變化,您還希望從應用程序中也開始支持MySQL。 只需將軟件包規格更改為以下內容,然后開始修復jOOQ使用中的所有編譯錯誤。
// Both dialects are allowed, no others are @Allow({ MYSQL, ORACLE })// Both dialects are also required on each clause @Require({ MYSQL, ORACLE }) package org.jooq.example.checker;默認值
默認情況下,對于任何范圍, org.jooq.checker.SQLDialectChecker都采用以下注釋:
- 什么都不允許。 每個@Allow批注都會添加到允許的方言集中。
- 一切都是必需的。 每個@Require批注將從必填方言集中刪除。
實際觀看
這些功能將是jOOQ 3.9的組成部分。 只需添加以下依賴項即可使用它們:
<dependency><!-- Use org.jooq for the Open Source editionorg.jooq.pro for commercial editions, org.jooq.pro-java-6 for commercial editions with Java 6 support,org.jooq.trial for the free trial edition --><groupId>org.jooq</groupId><artifactId>jooq-checker</artifactId><version>${org.jooq.version}</version> </dependency>…,然后為您的編譯器插件選擇適當的注釋處理器。
不能等到jOOQ 3.9嗎? 不用了 只需從GitHub上檢查3.9.0-SNAPSHOT版本,然后按照此處給出的示例項目進行操作:
- https://github.com/jOOQ/jOOQ/tree/master/jOOQ-examples/jOOQ-checker-framework-example
做完了! 從現在開始,使用jOOQ時,您可以確保編寫的任何代碼都可以在計劃支持的所有數據庫上運行!
我認為,今年的Annotatiomaniac冠軍頭銜應該交給檢查框架的制定者:
有關檢查器框架的更多信息:
- http://types.cs.washington.edu/checker-framework/
- http://eisop.uwaterloo.ca/live#mode=display(實時演示)
翻譯自: https://www.javacodegeeks.com/2016/05/jsr-308-checker-framework-add-even-typesafety-jooq-3-9.html
總結
以上是生活随笔為你收集整理的JSR-308和Checker框架为jOOQ 3.9添加了更多类型安全性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓车载导航软件下载(安卓车载导航软件)
- 下一篇: Thymeleaf 3 – Thymel