`
deepinmind
  • 浏览: 445921 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:40944
社区版块
存档分类
最新评论

Java 8:ORM已经过时了

阅读更多
ORM已经过时了

最近几十年来,关于ORM究竟还有没有用的争论一直不断。很多人承认Hibernate和JPA确实很好的解决了不少实际的问题(通常是复杂对象的持久化),但有些人认为,对于面向数据的应用而言,复杂的映射关系则有点大材小用了。

JPA通过在目标类型上使用硬编码的注解,来建立标准的声明式的映射规则,进而完成映射关系。但我们认为,很多以数据为中心的应用不应该受限于注解的局限性,而应该通过一种函数式的方式来解决。Java 8的Steam API终于让我们可以用一种简洁的方式来解决这个问题了!

我们先从一个简单的例子开始,这里我们使用H2的INFORMATION\_SCHEMA来查询所有的表及字段。我们专门用一个Map<String, List<String>>类型的数据结构来存储这个信息。我们用jOOQ来简化SQL的交互。下面来做一下准备工作:

public static void main(String[] args)
throws Exception {
    Class.forName("org.h2.Driver");
    try (Connection c = getConnection(
            "jdbc:h2:~/sql-goodies-with-mapping", 
            "sa", "")) {
 
        // This SQL statement produces all table
        // names and column names in the H2 schema
        String sql =
            "select table_name, column_name " +
            "from information_schema.columns " +
            "order by " +
                "table_catalog, " +
                "table_schema, " +
                "table_name, " +
                "ordinal_position";
 
        // This is jOOQ's way of executing the above
        // statement. Result implements List, which
        // makes subsequent steps much easier
        Result<Record> result =
        DSL.using(c)
           .fetch(sql)
    }
}


我们已经执行完查询了,现在来看下如何从查询结果中生成Map<String, List<String>>信息。

DSL.using(c)
   .fetch(sql)
   .stream()
   .collect(groupingBy(
       r -> r.getValue("TABLE_NAME"),
       mapping(
           r -> r.getValue("COLUMN_NAME"),
           toList()
       )
   ))
   .forEach(
       (table, columns) -> 
           System.out.println(table + ": " + columns)
   );


上述程序会输出如下的结果:

FUNCTION_COLUMNS: [ALIAS_CATALOG, ALIAS_SCHEMA, ...]
CONSTANTS: [CONSTANT_CATALOG, CONSTANT_SCHEMA, ...]
SEQUENCES: [SEQUENCE_CATALOG, SEQUENCE_SCHEMA, ...]


这是如何工作的?我们来一步一步的分析下:

DSL.using(c)
   .fetch(sql)
//这里我们把一个列表转化成一个Stream
 .stream() 
// 把Stream中的元素汇集成一个新的类型
   .collect(
// 收集器是一个分组操作,会生成一个map
            groupingBy(
// 分组操作的key是jOOQ记录的TABLE_NAME字段
       r -> r.getValue("TABLE_NAME"),
// 分组操作的值是用这个映射表达式来生成的
       mapping(
// 实际上是映射到每条jOOQ记录的COLUMN_NAME字段上
           r -> r.getValue("COLUMN_NAME"),
// 然后将所有的值收集到一个java.util.List中
           toList()
       )
   ))
// 一旦拿到了<String, List<String>>列表,
// 就可以很容易通过lambda表达式来使用它了
   .forEach(
       (table, columns) -> 
           System.out.println(table + ": " + columns)
   );


看明白了吗?第一次使用这些方法的话可能的确需要费点工夫。新的类型,泛型,lambda表达式,这些东西加到一起的话,第一次看到的话的确会有些困惑。最好的办法就是不断的去实践直到你完全掌握了。毕竟来说,和Java Collections API比起来的话,Stream API可是一次革命性的进步。

不过有个好消息就是,这些方法已经确定就是这样,不会再变了。你的每一次实践都是对未来的一次投资。

注意上述的程序用到了如下的静态导入语句:

import static java.util.stream.Collectors.*;


同样还应该注意到,输出的结果和数据库返回的顺序是不一样的。这是因为groupingBy收集器返回的是一个java.util.HashMap。在这个例子中,我们更希望能收集到一个java.util.LinkedHashMap里面,这个集合能保留插入时的顺序。

DSL.using(c)
   .fetch(sql)
   .stream()
   .collect(groupingBy(
       r -> r.getValue("TABLE_NAME"),
 
       // Add this Supplier to the groupingBy
       // method call
       LinkedHashMap::new,
       mapping(
           r -> r.getValue("COLUMN_NAME"),
           toList()
       )
   ))
   .forEach(...);


我们还可以用另一种转化结果的方法。想像一下,我们将要从上面的schema中生成DDL。这很简单。首先,我们需要知道每一列的数据类型。只需要把它加到我们的SQL查询语句中就好了:

String sql =
    "select " +
        "table_name, " +
        "column_name, " +
        "type_name " + // Add the column type
    "from information_schema.columns " +
    "order by " +
        "table_catalog, " +
        "table_schema, " +
        "table_name, " +
        "ordinal_position";


这个例子中还引入了一个新的局部类,用来封装名字和类型属性:

class Column {
    final String name;
    final String type
    Column(String name, String type) {
        this.name = name;
        this.type = type;
    }
}


现在我们来看下Streams API的方法调用该如何修改:

result
    .stream()
    .collect(groupingBy(
        r -> r.getValue("TABLE_NAME"),
        LinkedHashMap::new,
        mapping(
 
            // We now collect this new wrapper type
            // instead of just the COLUMN_NAME
            r -> new Column(
                r.getValue("COLUMN_NAME", String.class),
                r.getValue("TYPE_NAME", String.class)
            ),
            toList()
        )
    ))
    .forEach(
        (table, columns) -> {
 
            // Just emit a CREATE TABLE statement
            System.out.println(
                "CREATE TABLE " + table + " (");
 
            // Map each "Column" type into a String
            // containing the column specification,
            // and join them using comma and
            // newline. Done!
            System.out.println(
                columns.stream()
                       .map(col -> "  " + col.name +
                                    " " + col.type)
                       .collect(Collectors.joining(",\n"))
            );
 
            System.out.println(");");
        }
    );


输出的结果简直是棒极了!

CREATE TABLE CATALOGS(
  CATALOG_NAME VARCHAR
);
CREATE TABLE COLLATIONS(
  NAME VARCHAR,
  KEY VARCHAR
);
CREATE TABLE COLUMNS(
  TABLE_CATALOG VARCHAR,
  TABLE_SCHEMA VARCHAR,
  TABLE_NAME VARCHAR,
  COLUMN_NAME VARCHAR,
  ORDINAL_POSITION INTEGER,
  COLUMN_DEFAULT VARCHAR,
  IS_NULLABLE VARCHAR,
  DATA_TYPE INTEGER,
  CHARACTER_MAXIMUM_LENGTH INTEGER,
  CHARACTER_OCTET_LENGTH INTEGER,
  NUMERIC_PRECISION INTEGER,
  NUMERIC_PRECISION_RADIX INTEGER,
  NUMERIC_SCALE INTEGER,
  CHARACTER_SET_NAME VARCHAR,
  COLLATION_NAME VARCHAR,
  TYPE_NAME VARCHAR,
  NULLABLE INTEGER,
  IS_COMPUTED BOOLEAN,
  SELECTIVITY INTEGER,
  CHECK_CONSTRAINT VARCHAR,
  SEQUENCE_NAME VARCHAR,
  REMARKS VARCHAR,
  SOURCE_DATA_TYPE SMALLINT
);


激动吧?看来ORM的时代已经过去了

ORM的时代应该终结了。为什么?因为软件工程里有一个很重要的思想就是使用函数式表达式来进行数据集的转化。函数式编程表达性强,并且非常通用。它是数据及数据流处理的核心。Java开发人员现在也都知道函数式编程,而大家又都用过SQL。相像一下吧。你用SQL来声明表来源,把数据转化成新的元组流,然后要么将它们作为派生表提供给其它更高级的SQL语句来使用,要么将它们交给你的应用程序来处理。

如果你用的是XML的话,你可以使用XSLT来声明XML转化,并通过XProc管道把结果提供给其它XML处理器,比如说另一个XSL表格。

使用SQL和Streams API是数据处理里面一个很重要的概念。如果你使用了jOOQ的话,你还可以进行类型安全的数据库访问及查询。想像一下如何使用jOOQ的流API来改写前面的代码,而不是直接使用SQL语句。

整个方法调用链会是一个流式的数据转化链,就像这样:

DSL.using(c)
   .select(
       COLUMNS.TABLE_NAME,
       COLUMNS.COLUMN_NAME,
       COLUMNS.TYPE_NAME
   )
   .from(COLUMNS)
   .orderBy(
       COLUMNS.TABLE_CATALOG,
       COLUMNS.TABLE_SCHEMA,
       COLUMNS.TABLE_NAME,
       COLUMNS.ORDINAL_POSITION
   )
   .fetch()  // jOOQ ends here
   .stream() // Streams start here
   .collect(groupingBy(
       r -> r.getValue(COLUMNS.TABLE_NAME),
       LinkedHashMap::new,
       mapping(
           r -> new Column(
               r.getValue(COLUMNS.COLUMN_NAME),
               r.getValue(COLUMNS.TYPE_NAME)
           ),
           toList()
       )
   ))
   .forEach(
       (table, columns) -> {
            // Just emit a CREATE TABLE statement
            System.out.println(
                "CREATE TABLE " + table + " (");
 
            // Map each "Column" type into a String
            // containing the column specification,
            // and join them using comma and
            // newline. Done!
            System.out.println(
                columns.stream()
                       .map(col -> "  " + col.name +
                                    " " + col.type)
                       .collect(Collectors.joining(",\n"))
            );
 
           System.out.println(");");
       }
   );


Java 8代表着未来,而有了jOOQ,Java 8以及Streams API,你可以写出强大的数据转化的API。希望你能和我们一样感到激动!还有更多的Java 8的内容,敬请收看。


原创文章转载请注明出处:http://it.deepinmind.com

英文原文链接

3
3
分享到:
评论
17 楼 LinApex 2014-06-07  
还不如 Groovy,建议 Java 放弃 lamp,采用 函数式编程吧!
16 楼 fair_jm 2014-04-16  
思路就是和slick2一样吧 虽然没读过slick2源码
让sql和操作List一样 JDK8支持了lambda 加入了stream也可以实现
好像有个jinq的项目就是为了完成这一点的 ~~
15 楼 石中玉 2014-04-15  
ORM没那么快过时
14 楼 rainsilence 2014-04-14  
脑残的写法。
13 楼 alvin198761 2014-04-14  
这哥太幽默了,这是失传已久的<洛书河图>吗?
考古学家才能看懂啊
12 楼 liu78778 2014-04-14  
satuo20 写道
我们是这样封装:
CCarModelTB TCCarModel = Tables.get(CCarModelTB.class);
List<CCarModel> carModels = db
.select(TCCarModel.all)
.from(TCCarModel)
.where(TCCarModel.id.gt(1))
.orderBy(TCCarModel.id).desc()
.limit(10)
.queryObjectsForList(CCarModel.class);
System.err.println(carModels);


还不如写sql, 这有什么分别.

另外,ORM出来这么多年,也没有人说jdbc已经过时了,那么多简单高效的dbutils,jdbctemplate还是有大批的人在使用.
11 楼 satuo20 2014-04-14  
我们是这样封装:
CCarModelTB TCCarModel = Tables.get(CCarModelTB.class);
List<CCarModel> carModels = db
.select(TCCarModel.all)
.from(TCCarModel)
.where(TCCarModel.id.gt(1))
.orderBy(TCCarModel.id).desc()
.limit(10)
.queryObjectsForList(CCarModel.class);
System.err.println(carModels);
10 楼 justdojava 2014-04-14  
还是觉得ORM配合DAO可读性,可维护性,可扩展性更强一些。

object.setB("b");
object.saveOrUpdate();//update modifyTime auto


objectDao.filter("a",a).sortByName("createTime").select(10).limit(10).fetch("b").json();//common utils in the basicDAOClass

9 楼 SMCwwh 2014-04-14  
这尼玛跟JQuery代码似的
8 楼 1927105 2014-04-14  
ORM解决的是中小型项目,大型项目必须JDBC
7 楼 sayji 2014-04-13  
java是动起来了,可读性却降低了!
6 楼 cucaracha 2014-04-13  
感觉是给 jOOQ 打广告。
5 楼 youjianbo_han_87 2014-04-13  
学脚本语言学的,很多语法糖
4 楼 jahu 2014-04-13  
blooming 写道
这样的代码写的人爽读的人哭

笑死我了。。。。
在项目中。真心不敢使用这种编程方式。
3 楼 yixiandave 2014-04-13  
blooming 写道
这样的代码写的人爽读的人哭

+1.。。。嵌套那么多层,读起来真的会疯
2 楼 blooming 2014-04-13  
这样的代码写的人爽读的人哭
1 楼 ahack 2014-04-13  
老外真是幽默得很

相关推荐

    JAVA企业级开发:ORM对象映射.ppt

    JAVA企业级开发:ORM对象映射.ppt

    Ebean支持快速数据访问和编码的JavaORM框架

    Ebean:开源ORM框架 Ebean是一个Java实现的开源ORM框架,具有数据访问快速和易于学习、使用等特点。

    java mongodb orm 实现-基于mongodb-driver开发的ORM.pdf

    java mongodb orm 实现-基于mongodb-driver开发的ORM

    Java8应用封装,手写ORM,LOG,framework

    ##公用资源 ###敏捷第一,提供丰富语法糖,方法化,片段化 #####手写容器 #####手写ORM #####手写Log #####自动化配置 #####常用工具类 #####大量回调处理

    Hibernate框架ORM的实现原理

    Hibernate框架ORM的实现原理.doc

    Java自定义的类似ORM的数据库操作框架

    自己定义的类似ORM的MySQL数据库增伤改查的框架,直接配置session就能使用,简单的增删改查不用再写sql,方便、灵活。

    基于Java的简易ORM框架项目.zip

    随着信息技术的不断发展,企业对于数据的管理和操作需求日益增长。传统的JDBC(Java Database Connectivity)虽然可以实现...本项目旨在开发一个基于Java的简易ORM框架,为企业提供轻量级、高效的数据访问解决方案。

    KittenORM:ORM示例

    KittenORM:ORM示例

    Elasticsearch​的ORM工具orm4es.zip

    orm4es是一个Elastic... 例如:java -jar orm4es-0.0.1-SNAPSHOT.jar -H 192.168.1.100 -p 9300 -n Product -i product就可生成product.java类,你还可以对生成的类再次进行编辑。 标签:orm4es

    高性能的开源Java ORM框架-Ujorm

    Ujorm 它是一个高性能的开源Java ORM框架,尤其针对持久层的快速开发,在搜索查询的测试中,Ujorm的运行速度比其他竞争产品要快很多。 Ujorm在很多方面展开创新,在和Hibernate的比较中,Ujorm也拥有一些优势。...

    Java的ORM框架jOOQ.zip

    jOOQ 高效的合并了复杂SQL、类型安全、源码生成、Active Records、存储过程以及高级数据类型的 Java 类库。 示例代码: // Create a new record and insert it into the database TBookRecord book = create.new...

    java轻量级ORM实现-jorm (Just ORM)

    This project is based on Java, is a lightweight ORM model. Only concerned about the Object-Relationl Mapping, therefore more simple and easier to use, easier to control. Key support functions and ...

    XMLUtil java XML orm toolkit

    java xml orm 工具 XMLUtil java XML orm toolkit

    Aniki:ORM作为我们的哥哥

    Aniki:ORM作为我们的哥哥

    编程语言+JAVAspring+ORM框架+数据持久化

    编程语言+JAVAspring+ORM框架+数据持久化**:这是一个关于JAVAspring编程语言的ORM框架的数据持久化的资源,适合有一定JAVAspring基础的开发者。它介绍了JAVAspring的ORM框架的概念、原理和作用,以及如何使用JAVA...

    orm-wrapper:ORM 包装器

    ORM 包装器 ORM 多连接包装器。 Javascript 接口,允许您管理 node-orm2 模块的多个数据库连接。 入门 使用以下命令安装模块: npm install orm-wrapper 模型目录结构 . //Project root +-- models | +-- db1 | +-...

    java-orm:java开发的orm框架

    奥姆 这是一个移动游戏orm框架,可以帮助开发者避免编写无聊的原始SQL来保存玩家的个人数据。 通过简单的注释,开发人员现在可以专注于函数而不是基本的sql操作。 包括简单的junit测试。

    基于Java的IDEA ORM代码生成插件设计源码

    本设计源码提供了一个基于Java的IDEA ORM代码生成插件,包含75个文件,其中39个java源文件,14个ftl模板文件,6张png图片,3个md文档,2个gradle文件,2个form文件,2个txt文件,1个gitignore文件,1个LICENSE文件和...

    基于Java的hibernate ORM作业仓库设计源码

    本项目是基于Java的hibernate ORM作业仓库设计源码,包含29个文件,其中包括20个JAR文件、5个Java文件、2个XML文件、1个Gitignore文件和1个Iml文件。这个项目是一个ORM作业仓库,旨在通过Java技术栈实现,提供高效的...

Global site tag (gtag.js) - Google Analytics