jooq_jOOQ API设计缺陷的怪异事件

jooq

jOOQ是内部特定于域的语言(DSL) ,它以Java(宿主语言)建模SQL语言(外部DSL)。 这篇热门文章描述了jOOQ API的主要机制:

Java Fluent API设计器速成课程

任何人都可以根据该文章中的规则以Java(或大多数其他宿主语言)实现内部DSL。

SQL语言功能示例:BOOLEANs

但是,关于SQL语言的BOOLEANBOOLEAN类型,该类型在SQL:1999以后才引入到该语言中。 当然,没有布尔值,您可以通过10建模TRUEFALSE值,并使用CASE将谓词转换为值

CASE WHEN A = B THEN 1 ELSE 0 END

但是有了真正的BOOLEAN支持,您可以进行出色的查询,例如针对Sakila数据库运行的以下PostgreSQL查询:

SELECT
  f.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的ConditionField<Boolean>确实是同一回事。 jOOQ允许通过显式API在两者之间进行转换:

Condition condition1 = FIRST_NAME.like("%A%");
Field<Boolean> field = field(condition1);
Condition condition2 = condition(field);

…和重载使转换更方便地隐式进行。

所以有什么问题?

问题在于,我们认为最好再添加一个方便的重载,即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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值