vue.jsr入门
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类型系统的规则! 通过某种方式使注解图灵变得完整。#55357;�
这对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
vue.jsr入门