Java 8 Friday:不再需要ORM

Data Geekery ,我们喜欢Java。 而且,由于我们真的很喜欢jOOQ的流畅的API和查询DSL ,我们对Java 8将为我们的生态系统带来什么感到非常兴奋。

Java 8星期五

每个星期五,我们都会向您展示一些不错的教程风格的Java 8新功能,这些功能利用了lambda表达式,扩展方法和其他好东西。 您可以在GitHub上找到源代码

不再需要ORM

在过去的十年中,关于ORM(对象关系映射)的有用性的争论一直在进行。 尽管许多人会同意Hibernate和JPA很好地解决了许多问题(大多数情况是复杂对象图的持久性),但其他人可能会认为, 映射复杂性对于以数据为中心的应用程序来说大多数是过分的

JPA通过在接收目标类型上使用硬连线的注释建立标准化的声明性映射规则来解决映射问题。 我们声称,许多以数据为中心的问题不应仅受这些注释的狭窄范围的限制,而应以更加实用的方式解决。 Java 8和新的Streams API最终使我们能够以非常简洁的方式执行此操作!

让我们从一个简单的示例开始,在该示例中,我们使用H2的INFORMATION_SCHEMA收集所有表及其列。 我们将要生成一个Map<String, List<String>>类型的临时数据结构来包含此信息。 为了简化SQL交互,我们将使用jOOQ (与往常一样,此博客上的内容令人震惊)。 这是我们准备的方法:

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)
    }
}

现在我们已经设置了该查询,让我们看看如何从jOOQ Result中生成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)

// Here, we transform a List into a Stream
   .stream()

// We're collecting Stream elements into a new
// collection type
   .collect(

// The Collector is a grouping operation, producing
// a Map
            groupingBy(

// The grouping operation's group key is defined by
// the jOOQ Record's TABLE_NAME value
       r -> r.getValue("TABLE_NAME"),

// The grouping operation's group value is generated
// by this mapping expression...
       mapping(

// ... which is essentially mapping each grouped
// jOOQ Record to the Record's COLUMN_NAME value
           r -> r.getValue("COLUMN_NAME"),

// ... and then collecting all those values into a
// java.util.List. Whew
           toList()
       )
   ))

// Once we have this List<String, List<String>> we
// can simply consume it with the following Consumer
// lambda expression
   .forEach(
       (table, columns) -> 
           System.out.println(table + ": " + columns)
   );

得到它了? 第一次玩这些东西时,肯定有些棘手。 起初,新类​​型,泛型泛型,lambda表达式的组合可能会有些混乱。 最好的办法就是简单地练习这些东西,直到您掌握了它。 毕竟,与以前的Java Collections API相比,整个Streams API确实是一场革命。

好消息是:此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(...);

我们可以继续使用其他转换结果的方法。 想象一下,我们想根据上述模式生成简单的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语句或Java程序。

如果使用XML,则可以使用XSLT声明XML转换,并使用XProc pipelining将结果馈送到其他XML处理实体,例如另一个XSL样式表。

Java 8的Streams没什么。 使用SQL和Streams API是最强大的数据处理概念之一。 如果将jOOQ添加到堆栈,则可以从对数据库记录和查询API的类型安全访问中受益。 想象一下使用jOOQ的流畅API而不是使用SQL字符串编写上一条语句。

jooq-在Java中编写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内容。

翻译自: https://www.javacodegeeks.com/2014/04/java-8-friday-no-more-need-for-orms.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值