Java 8: No More Need for ORMs

Java 8, and the new Streams API finally allow us to solve data-centric problems in a very concise manner. Lukas Eder breaks it all down.

This post was originally published over at jooq.org as part of a special series
focusing on all things Java 8, including how take advantage of
lambda expressions, extension methods, and other great
stuff. You’ll find the
source code on GitHub.

JPA solves mapping problems by establishing standardised,
declarative mapping rules through hard-wired annotations on the
receiving target types. We claim that many data-centric problems
should not be limited by the narrow scope of these annotations, but
be solved in a much more functional way. Java 8, and the new
Streams API finally allow us to do this in a very concise
manner!

Let’s start with a simple example, where we’re using
H2′sINFORMATION_SCHEMA to collect all tables and
their columns. We’ll want to produce an ad-hoc data structure of
the type Map<String,
List<String>> to contain this information. For
simplicity of SQL interaction, we’ll use jOOQ (as always, a shocker on this
blog). Here’s how we prepare this:

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

Got it? These things are certainly a bit tricky when playing
around with it for the first time. The combination of new types,
extensive generics, lambda expressions can be a bit confusing at
first. The best thing is to simply practice with these things until
you get a hang of it. After all, the whole Streams API is really a
revolution compared to previous Java Collections APIs.

The good news is: This API is final and here to stay. Every
minute you spend practicing it is an investment into your own
future.

Note that the above programme used the following static
import:

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

Note also, that the output was no longer ordered as in the
database. This is because
the groupingBy collector returns
a java.util.HashMap. In our case, we might prefer
collecting things into a java.util.LinkedHashMap,
which preserves insertion / collection order:

We could go on with other means of transforming results. Let’s
imagine, we would like to generate simplistic DDL from the above
schema. It’s very simple. First, we’ll need to select column’s data
type. We’ll simply add it to our SQL query:

Excited? The ORM era may have ended just now

This is a strong statement. The ORM era may have ended. Why?
Because using functional expressions to transform data sets is one
of the most powerful concepts in software engineering. Functional
programming is very expressive and very versatile. It is at the
core of data and data streams processing. We Java developers
already know existing functional languages. Everyone has used SQL
before, for instance. Think about it. With SQL, you declare table
sources, project / transform them onto new tuple streams, and feed
them either as derived tables to other, higher-level SQL
statements, or to your Java program.

If you’re using XML, you can declare XML transformation using
XSLT and feed results to other XML processing entities, e.g.
another XSL stylesheet, using XProc pipelining.

Java 8′s Streams are nothing else. Using SQL and the Streams API
is one of the most powerful concepts for data processing. If you
add jOOQ to the stack,
you can profit from typesafe access to your database records and
query APIs. Imagine writing the previous statement using jOOQ’s
fluent API, instead of using SQL strings.

The whole method chain could be one single fluent data
transformation chain as such: