Java 8 彻底改变数据库访问 已翻译 100%

oschina 投递于 2014/03/26 08:01 (共 8 段, 翻译完成于 03-26)
阅读 19481
收藏 226
19
加载中

Java 8终于到来了! 经过几年的等待, java程序员终于能在java中得到函数式编程的支持了. 函数式编程的支持能流程化现有的代码并且为java提供强大的能力.在这些新特性中最瞩目的是java程序员对数据库的操作方式.函数式编程带来了令人激动的简便高效的数据库API. Java 8 将会支持可与像C#的LINQ等语言竞争的新的数据库访问方式.

处理数据的函数式方式

Java 8 不仅仅添加了函数式支持,它也通过新的函数式处理数据的方式扩展了集合(Collection)类. 而通常情况下java处理大量数据时需要大量的循环和迭代器.

pseudo
pseudo
翻译于 2014/03/26 09:15
3

例如, 假设你有一个存储客户(Customer)对象的collection:

Collection<Customer> customers;

如果你只对来自Belgium的客户感兴趣, 你将不得不迭代所有的customer对象并只保存你需要的.

Collection<Customer> belgians = new ArrayList<>();
for (Customer c : customers) {
    if (c.getCountry().equals("Belgium"))
        belgians.add(c);
}

这不仅花费了5行代码,而且它也不怎么抽象.假使你有1千万个对象时会怎样呢?你会通过两个线程并发过滤所有对象来提速么?那你将不得不使用大量危险的多线程代码来重写所有代码.

而通过Java 8,仅仅只需要一行代码就能实现相同的功能.通过对函数式编程的支持, Java 8 能让你只写一个函数表明你对哪些客户(对象)感兴趣然后使用那个函数对集合做过滤就可以了. Java 8 的新 Steams API 支持你这样做:

customers.stream().filter(
    c -> c.getCountry().equals("Belgium")
);

上面Java 8 版本的代码不仅更短,而且更容易理解.它几乎没有什么 陈词滥调(循环或迭代器等).代码调用了filter()方法,那很明显这段代码是用来过滤客户(对象)的.你不需要再把时间浪费在解读循环中的代码来理解它在对它的数据做什么.

假使你想并发执行这段代码该怎么办呢?你只需使用另一个类型的stream

customers.parallelStream().filter(
    c -> c.getCountry().equals("Belgium")
);

更另人激动的是这种函数式风格的代码也同样适用于数据库

pseudo
pseudo
翻译于 2014/03/26 09:55
4

在数据库上使用函数式方式

传统上来说, 程序员需要用特殊数据库查询语句去访问数据库的数据. 例如,下面就是用 JDBC 代码去查找来自Belgium的客户:

PreparedStatement s = con.prepareStatement(
      "SELECT * "
    + "FROM Customer C "
    + "WHERE C.Country = ? ");
s.setString(1, "Belgium");
ResultSet rs = s.executeQuery();

大部分这些代码都是字符串, 这样会使编译器不能发现错误而且这草率的代码会导致安全问题. 还有这些大量的样板代码使得写数据访问代码变得十分冗余. 一些工具例如 jOOQ ,通过使用特殊的java库去提供数据库查询语言可以解决错误检查和安全问题。 或者使用对象关系映射工具可以免去大量的无趣的代码,可它们只能用在通用访问查询, 如果需要复杂的查询,还是需要用特殊的数据库查询语言。

使用Java 8,借助流式API就可以用函数式方式去查询数据库了。例如, Jinq 是一个开源的项目,它探索怎样的未来数据库API可以令函数式编程成为可能。这里就是一个使用Jinq的数据库查询:

customers.where(
    c -> c.getCountry().equals("Belgium")
);

这代码几乎跟跟使用流式API的代码一样. 事实上,未来的Jinq版本可以让你用流式API直接写数据库查询。 当代码运行的时候,Jinq将自动翻译成数据库查询代码,正如之前JDBC查询一样。

Idiot_s_Sky
Idiot_s_Sky
翻译于 2014/03/26 10:42
5

这样的话,就算没有学过一些新的数据库查询语言,你也可以写出有效率的数据库查询。你可以用同样样式的代码用在java集合上。你也不需要特殊的java编译器或者虚拟机。所有的代码编译和运行在普通的java 8 JDK上。如果你的代码有错误,编译器将找出它们并且报告给你,就像普通的java代码。

Jinq 支持跟SQL92一样的复杂查询. Selection(选择), projection(投影), joins(连接), 和子查询 它都支持。翻译java代码成数据库查询的算法是十分灵活的,只要是它能接受的,都能翻译。例如,Jinq能够翻译下面的数据库查询,尽管它很复杂。

customers
    .where( c -> c.getCountry().equals("Belgium") )
    .where( c -> {
        if (c.getSalary() < 100000)
            return c.getSalary() < c.getDebt();
        else
            return c.getSalary() < 2 * c.getDebt();
        } );

正如你看到的,java 8 的函数式编程非常适合数据库查询。而且查询紧凑,甚至复杂的查询也能够胜任。

Idiot_s_Sky
Idiot_s_Sky
翻译于 2014/03/26 11:50
3

内部运作

但这都是如何工作的呢?怎么能让普通的Java编译器将Java代码转换成数据库查询?Java 8 有什么特别之处使这个成为可能?

支持这些函数性风格的新的数据库PI的关键是一种叫做“象征性执行”的字节码分析手段。虽然你的代码是被一个普通的Java编译器编译的并运行在一个普通的Java虚拟机中,但 Jinq 能够在你被编译的Java代码运行时进行分析并从中构建数据库查询。使用 Java 8 Streams API 时,常会发现分析短小的函数时,象征性执行的工作效果最好。

要了解这个象征性执行是如何工作的,最简单的方法是用一个例子。让我们检查一下下面的查询是如何被 Jinq 转换为SQL查询语言的:

customers
    .where( c -> c.getCountry().equals("Belgium") )

初始时, 变量 customers 是一个集合,其对应的数据库查询是:

SELECT *
  FROM Customers C
赵亮-碧海情天
赵亮-碧海情天
翻译于 2014/03/26 16:13
3

然后,where() 方法被调用,一个函数被传递给它。在 where() 方法中,Jinq 打开这个函数的 .class 文件,得到这个函数被编译成的字节码进行分析。在这个例子中,不使用真正的字节码,让我们用一些简单的指令来代表这个函数的字节码:

  1. d = c.getCountry()

  2. e = &#8220;Belgium&#8221;

  3. e = d.equals(e)

  4. return e

在这里,我们假设函数已被Java编译器编译成这四条指令。当调用 where() 方法时,Jinq 看到的就是这些。如何才能使Jinq理解这些代码呢?

Jinq 通过执行代码来分析。但 Jinq 不直接运行代码。它是“抽象”地运行代码:不使用真实的变量和真实的值,Jinq 使用符号来表示执行代码时的所有值。这就是这个分析为什么被称为“象征性执行”。

Jinq 执行每条指令,并跟踪所有的副作用或代码在程序状态时改变的所有东西。下面是一个图表,显示出 Jinq 用象征性执行方式执行这四行代码时发现的所有副作用。

象征性执行的例子

赵亮-碧海情天
赵亮-碧海情天
翻译于 2014/03/26 16:28
3

在图中,你可以看到第一条指令运行后,Jinq 发现了两个副作用:变量d已经发生了变化,方法 Customer.getCountry() 被调用。由于是象征性执行,变量d没有给出一个真正的比如是“USA”或“Denmark”的值,它被分配为 c.getCountry() 的象征性的值。

在所有这些指令被象征性执行之后,Jinq 对副作用作精简。由于变量 d 和 e 是局部变量,它们的任何变化在函数退出后都会被丢弃,所以这些副作用可以忽略不计。Jinq也知道 Customer.getCountry() and String.equals() 方法没修改任何变量或显示任何输出,因此这些方法调用也可以被忽略。由此,Jinq 可以得出这样的结论:执行这个函数只会产生一个作用,它会返回 c.getCountry().equals("Belgium")。

一旦Jinq已明白在 where()方法中传递给它的函数,它可以混合数据库查询方面的知识,优先于 customers 集合来创建一个新的数据库查询。

生成数据库查询

赵亮-碧海情天
赵亮-碧海情天
翻译于 2014/03/26 17:03
3

这就是 Jinq 如何从你的代码生成数据库查询的。象征性执行的使用意味着,这种方法对于不同的Java编译器输出的不同的代码模式都是相当强大的。如果 Jinq 遇到的代码有不能转化为数据库查询的副作用,Jinq 将保持你的这些代码不变。因为一切都是用正常的Java代码写的,Jinq 可以直接运行那些代码,您的代码将产生预期的结果。

这个简单的翻译实例应该让你明白了怎样查询翻译作品。你可以确信,这些算法可以正确地从你的代码生成数据库查询。

美好前景

我希望我已经让你品尝到了Java 8带来的在Java中进行数据库工作的新方式。Java 8 支持的函数式编程允许你用和为Java集合编写代码同样的方式来为数据库写代码。希望不久现有的数据库API都能被扩展以支持这些类型的查询。

赵亮-碧海情天
赵亮-碧海情天
翻译于 2014/03/26 15:57
3
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(111)

刘学炜
刘学炜

引用来自“GoodLoser”的评论

引用来自“19岁的天空”的评论

引用来自“GoodLoser”的评论

引用来自“19岁的天空”的评论

J8的这个特性在实用性上,楼主有些夸大了,说实话对J8很失望~~

你期望什么样的?还不够实用?你知道jdk7怎样写fork/join吗?你知道 jdk8的stream概念吗?他简化了并发代码,提升了并发性能!而且更符合我们表述问题的思维了

不要以为天下人什么都不知道,就你知道jdk7怎样写fork/join, jdk8的stream概念。J8并不优秀,这是客观事实,我也一直是JAVA开发者,但是JAVA和别的语言比起来,真的是堕落。

不要光说不练,你拿出证据来啊,怎么客观事实了,很纳闷你怎样成为java开发者的?

能写代码就行,不想用新特性,就特性还在,甭理喷子。学好还是自己的

天地一MADAO_
天地一MADAO_

引用来自“黑人牙膏”的评论

oracle把java玩死,BS Oracle,最终java变得不伦不类,以后看到什么好的都加上去,

我觉得这个也没什么不好的啊,至少旧的方式没禁用,不过多了一个选择的余地。就是可能到最后代码会变得五花八门各种团队不统一不适应。

天地一MADAO_
天地一MADAO_

其实挺好的,复杂的功能越来越简洁抽象了,不过在底层实现上有没有什么变化呢?

黑人牙膏
黑人牙膏

oracle当然不是故意要玩死java,只是这么做,例如增加接口默认方法实现,这个无疑是等同了多重继承的概念,抽象类就变得可有可无,

oralce是想java变得更好,但基本经过oracle手的开源的东西都最终下场很碉堡,
hudson,mysql,java,openoffice,这些原本sun的,无一都没有做好,企图闭源的,最终还是比不上开源分支

Anterior
Anterior

引用来自“黑人牙膏”的评论

引用来自“Anterior”的评论

引用来自“黑人牙膏”的评论

oracle把java玩死,BS Oracle,最终java变得不伦不类,以后看到什么好的都加上去,

感觉大神貌似还木有批评oracle的资本~

是否一个普通的java er就没资格批评某家公司?按照这个逻辑,你似乎也没资格批评我,即使你跟我是同个等级

抱歉,我不是在批评,普通的javaer可以去批判,但是应该说出自己的道理,你感觉oracle做的哪里不对,你感觉java加入函数式编程的特性有什么不妥,理性的分析一下,相信大家都会点赞。但是无缘无故的批判只会显得轻浮,抱歉我也是这样~oracle把好东西都加入到java就会把java玩死吗?你可以举例子证明给我看哦:)
王小费
王小费
不伦不类的,越搞越差
wffger
wffger

引用来自“NewJ”的评论

著名的语言学家 乔·布斯·霍金 曾说过,一切不过语言糖。

查不到
黑人牙膏
黑人牙膏

引用来自“Anterior”的评论

引用来自“黑人牙膏”的评论

oracle把java玩死,BS Oracle,最终java变得不伦不类,以后看到什么好的都加上去,

感觉大神貌似还木有批评oracle的资本~

是否一个普通的java er就没资格批评某家公司?按照这个逻辑,你似乎也没资格批评我,即使你跟我是同个等级
Anterior
Anterior

引用来自“黑人牙膏”的评论

oracle把java玩死,BS Oracle,最终java变得不伦不类,以后看到什么好的都加上去,

感觉大神貌似还木有批评oracle的资本~
终于19岁
终于19岁

引用来自“GoodLoser”的评论

引用来自“19岁的天空”的评论

引用来自“GoodLoser”的评论

引用来自“19岁的天空”的评论

J8的这个特性在实用性上,楼主有些夸大了,说实话对J8很失望~~

你期望什么样的?还不够实用?你知道jdk7怎样写fork/join吗?你知道 jdk8的stream概念吗?他简化了并发代码,提升了并发性能!而且更符合我们表述问题的思维了

不要以为天下人什么都不知道,就你知道jdk7怎样写fork/join, jdk8的stream概念。J8并不优秀,这是客观事实,我也一直是JAVA开发者,但是JAVA和别的语言比起来,真的是堕落。

不要光说不练,你拿出证据来啊,怎么客观事实了,很纳闷你怎样成为java开发者的?

我笑了~~~哈哈哈哈哈~~~
返回顶部
顶部