How JPA 2.1 has become the new EJB 2.0


Beauty lies in the eye of the beholder. So does “ease”:

Thorben writes very good and useful articles about JPA, and he’s recently started an excellent series about JPA 2.1’s new features. Among which: Result set mapping. You may know result set mapping from websites like CTMMC, or annotatiomania.com. We can summarise this mapping procedure as follows:

a) define the mapping

@SqlResultSetMapping(
    name = "BookAuthorMapping",
    entities = {
        @EntityResult(
            entityClass = Book.class,
            fields = {
                @FieldResult(name = "id", column = "id"),
                @FieldResult(name = "title", column = "title"),
                @FieldResult(name = "author", column = "author_id"),
                @FieldResult(name = "version", column = "version")}),
        @EntityResult(
            entityClass = Author.class,
            fields = {
                @FieldResult(name = "id", column = "authorId"),
                @FieldResult(name = "firstName", column = "firstName"),
                @FieldResult(name = "lastName", column = "lastName"),
                @FieldResult(name = "version", column = "authorVersion")})})

The above mapping is rather straight-forward. It specifies how database columns should be mapped to entity fields and to entities as a whole. Then you give this mapping a name ("BookAuthorMapping"), which you can then reuse across your application, e.g. with native JPA queries.

I specifically like the fact that Thorben then writes:

If you don’t like to add such a huge block of annotations to your entity, you can also define the mapping in an XML file

… So, we’re back to replacing huge blocks of annotations by huge blocks of XML – a technique that many of us wanted to avoid using annotations…🙂

b) apply the mapping

Once the mapping has been statically defined on some Java type, you can then fetch those entities by applying the above BookAuthorMapping

List<Object[]> results = this.em.createNativeQuery(
    "SELECT b.id, b.title, b.author_id, b.version, " +
    "       a.id as authorId, a.firstName, a.lastName, " + 
    "       a.version as authorVersion " + 
    "FROM Book b " +
    "JOIN Author a ON b.author_id = a.id", 
    "BookAuthorMapping"
).getResultList();

results.stream().forEach((record) -> {
    Book book = (Book)record[0];
    Author author = (Author)record[1];
});

Notice how you still have to remember the Book and Author types and cast explicitly as no verifiable type information is really attached to anything.

The definition of “complex”

Now, the article claims that this is “complex” mapping, and no doubt, I would agree. This very simple query with only a simple join already triggers such an annotation mess if you want to really map your entities via JPA. You don’t want to see Thorben’s mapping annotations, once the queries get a little more complex. And remember, @SqlResultSetMapping is about mapping (native!) SQL results, so we’re no longer in object-graph-persistence land, we’re in SQL land, where bulk fetching, denormalising, aggregating, and other “fancy” SQL stuff is king.

The problem is here:

Java 5 introduced annotations. Annotations were originally intended to be used as “artificial modifiers”, i.e. things like static, final, protected (interestingly enough, Ceylon only knows annotations, no modifiers). This makes sense. Java language designers could introduce new modifiers / “keywords” without breaking existing code – because “real” keywords are reserved words, which are hard to introduce in a language. Remember enum?

So, good use-cases for annotations (and there are only few) are:

  • @Override
  • @Deprecated (although, a comment attribute would’ve been fancy)
  • @FunctionalInterface

JPA (and other Java EE APIs, as well as Spring) have gone completely wacko on their use of annotations. Repeat after me:

No language @Before or @After Java ever abused annotations as much as Java tweet this

(the @Before / @After idea was lennoff’s, on reddit)

There is a strong déjà vu in me when reading the above. Do you remember the following?

No language before or after Java ever abused checked exceptions as much as Java

We will all deeply regret Java annotations by 2020.

Annotations are a big wart in the Java type system. They have an extremely limited justified use and what we Java Enterprise developers are doing these days is absolutely not within the limits of “justified”. We’re abusing them for configuration for things that we should really be writing code for.

Here’s how you’d run the same query with jOOQ (or any other API that leverages generics and type safety for SQL):

Book b = BOOK.as("b");
Author a = AUTHOR.as("a");

DSL.using(configuration)
   .select(b.ID, b.TITLE, b.AUTHOR_ID, b.VERSION,
           a.ID, a.FIRST_NAME, a.LAST_NAME,
           a.VERSION)
   .from(b)
   .join(a).on(b.AUTHOR_ID.eq(a.ID))
   .fetch()
   .forEach(record -> {
       BookRecord book = record.into(b);
       AuthorRecord author = record.into(a);
   });

This example combines both JPA 2.1’s annotations AND querying. All the meta information about projected “entities” is already contained in the query and thus in the Result that is produced by the fetch() method. But it doesn’t really matter, the point here is that this lambda expression …

record -> {
    BookRecord book = record.into(b);
    AuthorRecord author = record.into(a);
}

… it can be anything you want! Like the more sophisticated examples we’ve shown in previous blog posts:

Mapping can be defined ad-hoc, on the fly, using functions. Functions are the ideal mappers, because they take an input, produce an output, and are completely stateless. And the best thing about functions in Java 8 is, they’re compiled by the Java compiler and can be used to type-check your mapping. And you can assign functions to objects, which allows you to reuse the functions, when a given mapping algorithm can be used several times.

In fact, the SQL SELECT clause itself is such a function. A function that transforms input tuples / rows into output tuples / rows, and you can adapt that function on the fly using additional expressions.

There is absolutely no way to type-check anything in the previous JPA 2.1 native SQL statement and @SqlResultSetMapping example. Imagine changing a column name:

List<Object[]> results = this.em.createNativeQuery(
    "SELECT b.id, b.title as book_title, " +
    "       b.author_id, b.version, " +
    "       a.id as authorId, a.firstName, a.lastName, " + 
    "       a.version as authorVersion " + 
    "FROM Book b " +
    "JOIN Author a ON b.author_id = a.id", 
    "BookAuthorMapping"
).getResultList();

Did you notice the difference? The b.title column was renamed to book_title. In a SQL string. Which blows up at run time! How to remember that you have to also adapt

@FieldResult(name = "title", column = "title")

… to be

@FieldResult(name = "title", column = "book_title")

Conversely, how to remember that once you rename the column in your @FieldResult, you’ll also have to go check wherever this "BookAuthorMapping" is used, and also change the column names in those queries.

@SqlResultSetMapping(
    name = "BookAuthorMapping",
    ...
)

Annotations are evil

You may or may not agree with some of the above. You may or may not like jOOQ as an alternative to JPA, that’s perfectly fine. But it is really hard to disagree with the fact that:

  • Java 5 introduced very useful annotations
  • Java EE / Spring heavily abused those annotations to replace XML
  • We now have a parallel universe type system in Java
  • This parallel universe type system is completely useless because the compiler cannot introspect it
  • Java SE 8 introduces functional programming and lots of type inference
  • Java SE 9-10 will introduce more awesome language features
  • It now becomes clear that what was configuration (XML or annotations) should have been code in the first place
  • JPA 2.1 has become the new EJB 2.0: Obsolete

As I said. Hard to disagree. Or in other words:

Code is much better at expressing algorithms than configuration tweet this

I’ve met Thorben personally on a number of occasions at conferences. This rant here wasn’t meant personally, Thorben🙂 Your articles about JPA are very interesting. If you readers of this article are using JPA, please check out Thorben’s blog: http://www.thoughts-on-java.org.

In the meantime, I would love to nominate Thorben for the respected title “The Annotatiomaniac of the Year 2015

25 thoughts on “How JPA 2.1 has become the new EJB 2.0

  1. Over the years I’ve went from, “what would I use annotations for?”, to “Annotations are great!”, to “Annotations are terrible”

    Now I see JavaScript is proposing to add something like annotations (decorators) with ES7😦

  2. That’s why I prefer Hibernate over JPA. Hibernate has a simple query-time transforming utility such as:


    session.createSQLQuery(
    "SELECT b.id, b.title, b.author_id, b.version, " +
    " a.id as authorId, a.firstName, a.lastName, " +
    " a.version as authorVersion " +
    "FROM Book b " +
    "JOIN Author a ON b.author_id = a.id"
    ).setResultTransformer(Transformers.aliasToBean(BookAuthorMapping.class));

    You don’t need any annotation and it’s just plain old code.

    • Well, that Hibernate API doesn’t use any generics, nor functional interfaces. It’s hardly ready to keep up with modern development (“modern” for Java standards).

  3. One thing you didn’t mention is another alternative to annotations and XML for declarative programming. That is, a language feature that allows one to build an object graph declaratively. There’s an example of this on the homepage of http://ceylon-lang.org, and in the introduction: http://ceylon-lang.org/documentation/introduction/

    So, for a class that holds Configuration, I could write this:

    Configuration {
              key1="value1";
              key2="value2";
              SubConfiguration {
                      subkey1="subvalue1";
                      subkey2="subvalue2";
              }
    }
    

    All Java would need to support this is to allow named constructor and method arguments.

    • That’s a nice example. Some more modern languages do indeed allow for expressing concise DSLs in a similar way. On the other hand, that’s still configuration, not really (executable and debuggable) code. I just don’t think that’s right. Although, if you somehow managed to combine the object graph construction syntax with actual functions / code, that would be nice!

  4. How would you write the example with the book count (where a combination of @EntityResult & @ColumnResult is used) in jOOQ?

    I don’t get why you say “We now have a parallel universe type system in Java”, since, as the JLS says: “An annotation […] has no effect at run time.”

    I understand you want to promote jOOQ, but, in my opinion, your “hard-to-disagree facts” about the evilness of annotations & obsoleteness of JPA are downright ridiculous.

    • How would you write the example with the book count (where a combination of @EntityResult & @ColumnResult is used) in jOOQ?

      The same way. Roughly:

      Book b = BOOK.as("b");
      Author a = AUTHOR.as("a");
       
      DSL.using(configuration)
         .select(b.ID, b.TITLE, b.AUTHOR_ID, b.VERSION, count())
         .from(b)
         .join(a).on(b.AUTHOR_ID.eq(a.ID))
         .groupBy(b.ID, b.TITLE, b.AUTHOR_ID, b.VERSION)
         .fetch()
         .forEach(record -> {
             BookRecord book = record.into(b);
             Integer count = record.getValue(count());
         });
      

      But I’m sure you weren’t after an actual answer? I’ll happily respond to any other questions & doubts you may have as soon as you reach Paul Graham DH4 level or above (http://paulgraham.com/disagree.html).

      Cheers,
      Lukas

      • Ok, I admit that wasn’t very constructive. Allow me to elaborate.

        I disagree, because result set mappings are only needed for native SQL queries or stored procedures. And even then, only when you need to map to several classes/columns/entities at once.
        When using JPA, these cases are rather seldom (in our codebase with a few hundred entities, we currently have 4 occurrences). Even more so when the database schema is generated by JPA. Which brings me to my second point: when you let JPA generate the database schema from your code, the amount of required annotations is really small. And even when mapping to a legacy database (as in our case), it’s typically just a matter of adding 1-2 annotations (@Column, @JoinColumn & the likes).
        About the XML: as far as i know, it’s actually considered a best practice to consolidate the native queries in a separate orm.xml file. You could write unit tests to extract & execute all native queries from the orm.xml file and thus immediately catch any typos.
        The idea in Java EE is to use annotations, and override in XML where desired. Or in this case, use XML to consolidate non-portable SQL (switching to another RDBMS would simply require providing an alternative orm.xml for that RDBMS).
        You introduce the jOOQ code by saying “[…] with jOOQ (or any other API that leverages generics and type safety for SQL)”, but don’t mention that JPA also has a generified, type-safe Criteria API for the typical non-native case. And again: if you just keep all result set mappings & native queries in a single orm.xml file, it’s possible to unit-test everything, and renaming things isn’t hard at all.
        Finally, you end by listing things you consider facts that are hard to disagree with. First, there are several points which are simply overly general (& thus indeed, hard to disagree) statements about Java releases:
        – Java 5 introduced very useful annotations
        – Java SE 8 introduces functional programming and lots of type inference
        – Java SE 9-10 will introduce more awesome language features
        I’ll shortly go over the other points individually:
        – Java EE / Spring heavily abused those annotations to replace XML
        As said above, annotations aren’t meant to replace XML, they’re complementary. Moreover, “heavily abused” is your personal opinion, not a fact.
        – We now have a parallel universe type system in Java
        As mentioned in my first comment, the JLS says “An annotation […] has no effect at run time.”. It also doesn’t influence type inference in any way, so I don’t see why you classify it as a “parallel universe type system”?
        – This parallel universe type system is completely useless because the compiler cannot introspect it
        I’m no expert here, but the JDK “Compiler Tree API” does have a class named AnnotationTree ( http://docs.oracle.com/javase/8/docs/jdk/api/javac/tree/com/sun/source/tree/AnnotationTree.html ), so why do you say the compiler cannot introspect it?
        – It now becomes clear that what was configuration (XML or annotations) should have been code in the first place
        Again, I disagree. In my opinion, JPA mapping metadata is a perfect fit for annotations. As a non-JPA example: JAX-RS services are so easy to write & understand, that I don’t see how replacing the annotations with code would help in any way.
        – JPA 2.1 has become the new EJB 2.0: Obsolete
        I don’t know if this sentence was simply an exaggeration & meant to be thought-provoking in some way, but this is obviously not a fact. Assuming for a moment that this is indeed true, which technologies have replaced JPA in the Java – RDBMS persistence space?

        • That’s more like DH6, thanks🙂 And of course, you got many valid points

          JPA also has a generified, type-safe Criteria API for the typical non-native case

          I expressly omitted that because it is

          1) not as type safe as it could / should be.
          2) people don’t exactly love using that API. The EG could’ve done much much better.

          Of course, this is my opinion, only, but the Criteria API is one of the most poorly executed APIs in the history of Java EE.

          it’s possible to unit-test everything, and renaming things isn’t hard at all

          I’m looking forward to a response blog post with examples then, because those examples are very scarce. I usually see mostly how things go out of control in customer projects. I think that showing how renaming things isn’t hard at all with your approach would be extremely useful.

          you consider facts that are hard to disagree with

          Yes, clearly and intentionally polemic.

          I don’t see why you classify it as a “parallel universe type system”?

          Annotations as used by JPA create a type system where types are “externally” annotated with semantics that cannot be checked by the compiler because they’re in a “parallel universe”, or as you put it, because they have no effect at run time (or compile time). This is not what most modern programming languages embrace by making type systems as accessible as possible to API designers and consumers. In other words, there is no way you can easily reason about an entity attribute being an actual database column from its mere type at compile time.

          why do you say the compiler cannot introspect it

          You could engage in writing annotation processors (or Java 8 / JSR-308 checker framework implementations), but frankly, who does that? If the “regular” type system were used (e.g. via generics) more thoroughly, type information would be accessible to the programmer without any extra effort.

          The interesting discussion about this article on reddit (http://redd.it/32o9kp) compares Java annotation processors with more sophisticated languages’ macros and macro expansion tools. While annotations per se would be a sufficient approach at declaring the macros, the macro expansion toolkit is very poor and limited. Apart from Lombok, there haven’t been many successful implementations simply because it’s extremely hard to get anything out of these processors. I’m looking forward to see how JSR-308 and the checker framework performs, but I doubt that it will be widely adopted, when at the same time, you can return to the “primary” type system that now supports first-class-ish functions.

          Again, I disagree. In my opinion, JPA mapping metadata is a perfect fit for annotations

          Then, why does the Criteria API, which you’ve mentioned, rely on a spare set of metadata represented in ordinary types? It does, because annotations are defining metadata in a “parallel universe” type system, that is not accessible to APIs like the Criteria API at compile time. You will need redundant meta data.

          As a non-JPA example: JAX-RS services are so easy to write & understand, that I don’t see how replacing the annotations with code would help in any way.

          An example: http://sparkjava.com. Completely annotation-less, working only with functions. While I like JAX-RS, I still believe that annotating a method with REST metadata is less powerful than submitting a function to an entity that manages requests, merely because the latter is much more explicit, versatile, and concise.

          Of course, as you correctly observed, this argument will highly depend on whether one or the other style is preferred.

          I don’t know if this sentence was simply an exaggeration & meant to be thought-provoking in some way, but this is obviously not a fact. Assuming for a moment that this is indeed true, which technologies have replaced JPA in the Java – RDBMS persistence space?

          Everything in this article is both slightly exaggerated and thought-provoking, that’s how we write on this blog. We’re not a scientific, factual blog. We want to build opinions in our industry.

          Nothing in the article said that JPA per se is obsolete. JPA is very useful. But JPA 2.1 is. I’m very sure (and willing to take bets) that JPA 3.0 will be much less annotation-based, and will embrace what’s available since Java SE 8 – and if the timing’s right, also what will hopefully be available in Java SE 10.

          If functional programming has been embraced by .NET a long time ago, and finally by Java as well, it will be hard to fight it by keeping logic out of client code, relying on the implicitness of some spec that defines how annotations are to be interpreted. The opinion expressed here tends to say that metadata will move back from configuration into code, embracing a more useful, more unified type system.

          • Thanks for your extensive reply🙂

            I don’t know how to include the quotes, so I’ll do it the basic way: “I think that showing how renaming things isn’t hard at all with your approach would be extremely useful.”

            Since there’s an XSD for the JPA XML files, you can create a unit test to parse persistence.xml with JAXB, extract all referenced orm.xml files, loop over them, for each orm.xml, loop over all native queries, and execute them. You could add JPA hints in the query definition in orm.xml to provide test parameters to the unit tests (where name is something like “com.mycompany.test.p1” and value is a JavaScript fragment). Then simply loop over these hints, do an “eval” of their values with Nashorn, set the parameters & execute the queries. Since you’re actually executing all queries in your tests, any typos will be caught at build time.

            PS: there won’t be a response blog post (for the simple reason I don’t have one), but if you see flaws in my approach, please do let me know

          • I don’t know how to include the quotes

            It’s just a matter of applying <blockquote/>

            loop over all native queries, and execute them

            Execute them!? You and I, we’re clearly talking about a whole different set of application types. No way in hell I’d ever dare randomly execute statements from the applications that I provide consulting for… But I can see how that works for this particular use-case. Validates both metadata names and query syntax. How many queries are you talking about?

  5. Come on, JPA can do a lot of stuff on top of JDBC and you are only focusing on the things were it does not really shine (like SqlResultSetMapping for example).

    Each time you post something over JPA, you take simple select examples like querying the database and mapping the result to objects… but JPA does more than that: its EntityManager works, for example, as a unit of work were all changes applied on your entities are automatically saved to the database when you commit your transaction. I would really like to see how you do that with JOOQ because I could not live without it anymore. I have a fairly complex domain model with complicated business logic (hey, I work in e-government environment and some business logic is directly derived from laws which are always messy) and I wouldn’t want to mix my business logic with update calls to my database, nor would I want to create my own way of tracking changes on my entities to persist those at the end.

    So if you want people to use JOOQ instead of JPA, you should really offer them a FULL alternative of what they find useful in JPA. As you are talking about EJB2.0, Rod Johnson wrote a book about all things that were wrong in EJB2.x but he proposed a full alternative with his Spring framework.

    And don’t take me wrong, I like JOOQ: it’s nicely written, it can be used with new java 8 constructs and I can tell you have put a lot of energy and thoughts in it. There are various use cases where I would definitely use it but there’s no need to bash JPA all the time. I really thing JPA and JOOQ can be complementary as they are today. You don’t need to push JOOQ as an anti-JPA alternative.

    • First off: Don’t confuse wilful polemics that we use to make a point (and to attract readers), with the actual message that is hidden in such articles.

      You may have misunderstood the article, which was criticising only those JPA 2.1 features that are unnecessary or poorly executed. JPA as a whole (and specifically, its best implementation, Hibernate) is very useful. We’ve already covered that in a different article. jOOQ vs. Hibernate: When to Choose Which.

      This article claims that JPA 2.1 (specifically: its abuse of annotations) is obsolete and will be superseded by a better JPA 3.0, eventually and hopefully. That’s what it says. Nothing more. If you got more out of the article than this, then you may have fallen for our marketing techniques, which is also good, as it got you thinking about the topic, and bikeshedding the secondary parts of the article: The polemics🙂

      Just to be clear, one more time:

      So if you want people to use JOOQ instead of JPA, you should really offer them a FULL alternative of what they find useful in JPA

      That is not what we say. Neither option is the magic bullet. Much like Gavin King has ALWAYS made it clear that you shouldn’t use Hibernate (ORM) for everything, we make it clear that you shouldn’t use jOOQ (SQL) for everything. Of course, people keep looking for magic bullets and shoot themselves in their feet.

      but there’s no need to bash JPA all the time

      I’m sorry you feel this way. We have no intention of doing this “all the time”, and I truly believe that we actually don’t (most our articles are about Java SE, and about SQL). Nonetheless, criticism is important. It is essential for a vendor like ourselves to point out where alternative solutions fail. In a way, this will also benefit the alternative solutions. JPA 3.0 will be a lot of work.

      I really thing JPA and JOOQ can be complementary as they are today

      Absolutely. We got a couple of blog posts coming up on that topic. Stay tuned!

      • Fair enough, I will try to take your sensationalist titles with a grain of salt in the future… even if I do not condone those: comparing some technology with EJB2.0 is the worst insult I could come up with… or maybe Struts 1.x😉

        • I will try to take your sensationalist titles with a grain of salt in the future…

          Yes, please do so: https://xkcd.com/1283😉

          comparing some technology with EJB2.0 is the worst insult I could come up with…

          Really? I could’ve picked CORBA, instead. Besides, EJB 2.0 had a couple of good ideas, such as message driven beans…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s