在Java程序中使用数据库的新方法

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

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

例如, 假设你有一个存储客户(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")
);

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

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

传统上来说, 程序员需要用特殊数据库查询语句去访问数据库的数据. 例如,下面就是用 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查询一样。

这样的话,就算没有学过一些新的数据库查询语言,你也可以写出有效率的数据库查询。你可以用同样样式的代码用在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 的函数式编程非常适合数据库查询。而且查询紧凑,甚至复杂的查询也能够胜任。

内部运作

但这都是如何工作的呢?怎么能让普通的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

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

  d = c.getCountry()

  e = “Belgium”

  e = d.equals(e)

  return e

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

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

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

象征性执行的例子

在图中,你可以看到第一条指令运行后,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 集合来创建一个新的数据库查询。

生成数据库查询

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

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

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

(0)

相关推荐

  • JavaScript使用ActiveXObject访问Access和SQL Server数据库

    JS操作 Access 数据库 复制代码 代码如下: <SCRIPT LANGUAGE="JavaScript">  <!--  var filePath = location.href.substring(0, location.href.indexOf("实例197.连接Access数据库.html"));    //以当前页面文件为基础,找到文件所在的绝对路径  var path = filePath + "197.mdb"

  • Java使用JDBC连接数据库的实现方法

    本文实例讲述了Java使用JDBC连接数据库的实现方法,是Java数据库程序设计里非常实用的重要技巧.分享给大家供大家参考.具体如下: JDBC(Java Data Base Connectivity)数据库连接,通常我们在编写web应用或java应用程序要连接数据库时就要使用JDBC.使用JDBC连接数据库一般步骤有: 1.加载驱动程序 Class.forName(driver); 2.创建连接对象 Connection con = DriverManager.getConnection(ur

  • JavaScript中操作Mysql数据库实例

    //创建数据库连接对象 var conn = new ActiveXObject("ADODB.Connection"); //创建数据集对象 var rs = new ActiveXObject("ADODB.Recordset"); try{ //数据库连接串,具体配置请参考:http://www.connectionstrings.com/ //如果不知道如何配置连接串,可以通过配置UDL文件后用文本编辑器打开获得 var connectionstring =

  • JavaScript操作Oracle数据库示例

    我还是IT界的一只小菜鸟,参加工作时间不长,不过凭着自己的一颗好学的心还有自己永不停止的学习脚步,自己在编程方面也是收获颇丰~~ 一直以为JavaScript想和数据库交互必须通过AJAX来调用服务器端代码(C#或JAVA)才行,但最近才发现JavaScript可以直接与数据库进行交互...下面是一个简单的从数据库中调取数据加载到界面的小例子(有关JS操作数据方面的知识大家如有什么好的建议希望您能留下,我们相互学习,共同进步)~~ <!DOCTYPE HTML PUBLIC "-//W3C

  • JavaScript中连接操作Oracle数据库实例

    前言 无论是b/s还是c/s的开发中,基本上不使用javascript来对数据库进行操作.而我了印证我的一个想法,需要往数据库增加大量的新闻类信息,因此,我想从各个rss站点上获取信息并将信息导入到数据库里去.其实我也可以选择使用java,c++,或者是c#等编译语言,不过,使用javascript语言对这项工作来说是效率最高的.那我又何乐而不为呢? 环境 操作系统:winxp sp2 使用工具:cscript.exe,批处理文件 数据库:oracle 10g作为目标数据库(还可以使用其他的数据

  • Javascript连接Access数据库完整实例

    本文实例讲述了Javascript连接Access数据库的方法.分享给大家供大家参考.具体实现方法如下: var roc = roc || {}; roc.db = roc.db ||{}; //创建一个连接 roc.db.createDb = function(){ var conn = new ActiveXObject("ADODB.Connection"), fso = new ActiveXObject("Scripting.FileSystemObject&quo

  • Java实现数据库连接池的方法

    本文实例讲述了Java实现数据库连接池的方法.分享给大家供大家参考.具体如下: package com.kyo.connection; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import j

  • Java连接并操作Sedna XML数据库的方法

    本文实例讲述了Java连接并操作Sedna XML数据库的方法.分享给大家供大家参考.具体分析如下: Sedna 是一个原生的XML数据库,提供了全功能的核心数据库服务,包括持久化存储.ACID事务.索引.安全.热备.UTF8等.实现了 W3C XQuery 规范,支持全文搜索以及节点级别的更新操作. import ru.ispras.sedna.driver.*; public class SednaClient { public static void main(String args[])

  • Java 连接Access数据库的两种方式

    java连接MS Access的两种方式: 1.JDBC-ODBC Java连接Access可以使用MS自带的管理工具-->数据源(ODBC)设置建立连接,这样就不需要导入jar.但是,如此一来程序部署的每个机器上都要进行设置不方面.所以现在不会使用啦. 2.JDBC java也可以和连接其他数据库一样连接MS Access,导入数据库相应的jar包,进行连接. 复制代码 代码如下: java Access JDBC jar包:Access_JDBC30.jar 具体连接,参考下面代码: 复制代

  • java连接Oracle数据库的工具类

    一个封装好的链接Oracle数据库的工具类,可以方便的获取Connection对象关闭Statement.ResultSet.Statment对象等等 复制代码 代码如下: package myUtil; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLExcepti

  • Java中使用JDBC操作数据库简单实例

    好久没有编写有关数据库应用程序啦,这里回顾一下java JDBC. 1.使用Java JDBC操作数据库一般需要6步: (1)建立JDBC桥接器,加载数据库驱动: (2)连接数据库,获得Connection对象(使用数据库连接地址,用户名,密码): (3)获得数据库Statement对象: (4)执行数据库操作: (5)读取结果: (6)关闭数据库连接: 2.使用Java JDBC操作数据库(mysql)代码: 连接mysql数据库,需要导入mysql数据库jar包,本代码使用mysql-con

  • JavaScript实现的内存数据库LokiJS介绍和入门实例

    LokiJS是一个内存数据库,将性能考虑放在第一位. LokiJS支持索引和更快的文档访问,执行性能非常好(近50万OPS/秒).其内置DynamicView类可以用于数据子集的索引,甚至获取更快的性能. *阅读这篇文章来看一看LokiJS的性能表现. LokiJS支持collections(数据集),跟MongoDB的很像,并且以JSON格式将数据保存到磁盘,所以你的数据是可移植的. LokiJS即可运行在Node.js端和浏览器端. JavaScript是一种简单易学,通用的语言,所以在ja

  • JavaScript数据库TaffyDB用法实例分析

    本文实例讲述了JavaScript数据库TaffyDB用法.分享给大家供大家参考.具体如下: TaffyDB 是一个免费开源的 JavaScript 库,用于在 Web 上实现一个轻量级的数据访问层,也就是一个简单的数据库. 数据定义: var friends = new TAFFY( [ {name:"Bob", gender:"M", married:"No", age:25, state:"NY", favorite_f

  • Java连接六类数据库技巧全攻略

    小编在本文将主要为大家介绍Java与Oracle.DB2.Sql Server.Sybase.MySQL.PostgreSQL等数据库连接的方法. 1.Oracle数据库 Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); String url="jdbc:oracle:thin:@localhost:1521:orcl"; //orcl为数据库的SID String user="

  • java连接Mysql数据库的工具类

    一个封装好的链接Mysql数据库的工具类,可以方便的获取Connection对象关闭Statement.ResultSet.Statment对象等等 复制代码 代码如下: package myUtil; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLExceptio

随机推荐